无缓冲管道 :

指在接收前没有能力保存任何值的通道,这种类型通道要求发送gorouutine和接收goroutine同时准备好,才能完成发送和接收操作。如果两个goroutine没有同时准备好,

通道会导致先执行发送或者接收的goroutine阻塞等待,这种对通道进行发送和接收的交互行为本身就是同步的。其中任意一个操作都无法离开另一个操作单独存在

模拟打羽毛球:发球 接球

package main

import (
"fmt"
"math/rand"
"sync"
"time"
) var wg sync.WaitGroup func init(){
rand.Seed(time.Now().Unix())
} func main(){
court := make(chan int)
wg.Add() go player("发球",court)
go player("接球",court)
court <-
wg.Wait()
} func player(name string,court chan int) {
defer wg.Done() for{
ball,ok:= <-court
if !ok{
fmt.Printf("player %s won\n",name)
return
}
n:=rand.Intn()
if n% =={
fmt.Printf("player %s Missed\n",name)
close(court)
return
}
fmt.Printf("player %s Hit %d\n",name,ball)
ball++
court<- ball
}
}
GOROOT=D:\go #gosetup
GOPATH=D:\gospaces #gosetup
D:\go\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\___go_build_listen20_go.exe D:/gocode/test/listen20.go #gosetup
"D:\soft\goland2018.3\GoLand 2018.3.5\bin\runnerw64.exe" C:\Users\Administrator\AppData\Local\Temp\___go_build_listen20_go.exe #gosetup
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Hit
player 发球 Hit
player 接球 Missed
player 发球 won

4名跑步者围绕赛道轮流跑 接力棒

package main

import (
"fmt"
"sync"
"time"
) var wg sync.WaitGroup func main(){
//创建一个无缓冲的通道
baton := make(chan int)
wg.Add()
//第一位跑步者持有接力棒
go Runner(baton) //开始比赛
baton <- wg.Wait()
} //Runner 模拟接力比赛中的一位跑步者
func Runner(baton chan int){
var newRunner int
//等待接力棒
runner := <- baton
//开始绕着跑道跑步
fmt.Printf("运动员 %d 到跑到这里来准备... \n",runner) //创建下一位跑步者
if runner != {
newRunner = runner+
fmt.Printf("运动员 %d 开始跑... \n",runner)
go Runner(baton)
} //围绕着跑到跑步跑100毫秒
time.Sleep(*time.Microsecond) //比赛结束了吗?
if runner == {
fmt.Printf("运动员 %d finished ,race over \n",runner)
wg.Done()
return
} fmt.Printf("运动员 %d 跑完一圈了开始和运动员 %d 交换接力棒... \n",runner,newRunner)
baton <- newRunner
}

运动员 1 到跑到这里来准备...
运动员 1 开始跑...
运动员 1 跑完一圈了开始和运动员 2 交换接力棒...
运动员 2 到跑到这里来准备...
运动员 2 开始跑...
运动员 2 跑完一圈了开始和运动员 3 交换接力棒...
运动员 3 到跑到这里来准备...
运动员 3 开始跑...
运动员 3 跑完一圈了开始和运动员 4 交换接力棒...
运动员 4 到跑到这里来准备...
运动员 4 finished ,race over

有缓冲的通道:

有缓冲的通道是一种在被接收前能够存储一个或者多个值的通道,这种类型的通道并不强制要求协程之间必须同时完成发送和接收。通道会阻塞发送和接收的

动作的条件也会不同,只有在通道中没有要接受的值时,接收动作才会阻塞。只有在通道没有可用的缓冲的去容纳被发送的值时,发送才会阻塞。这回导致有缓冲

的通道和无缓冲的通道之间的一个很大的不同:无缓冲的通道保证进行发送和接收的协程会在同一时间进行数据交换;有缓冲的通道没有这种保证

package main

import (
"fmt"
"math/rand"
"sync"
"time"
) const(
numberGoroutines = //要使用的gotoutine的数量
taskLoad = //要处理的工作的数量
) //wg 用来等待程序完成
var wg sync.WaitGroup //优先执行这个函数
func init(){
rand.Seed(time.Now().Unix())
} //main 是所有go程序入口
func main(){
//创建一个有缓冲的通道来管理工作
tasks := make(chan string,taskLoad) //启动协程来处理工作
wg.Add(numberGoroutines)
for gr := ;gr<=numberGoroutines;gr++ {
go worker(tasks,gr)
} //增加一组要完成的工作
for post := ;post<=taskLoad;post++{
tasks <- fmt.Sprintf("task :%d",post)
} //当所有工作都处理完成是关闭管道
//以便所有goroutine推出
//很多人此处有疑问:为啥任务没处理完就关闭了
//答:当任务关闭后 协程依然可以从通道中接收数据,但是不能像通道发送数据,能够从已经关闭的通道接收数据这点非常非常重要
//因为这允许通道关闭后依旧能取出其中缓冲的全部值,而不丢失数据
close(tasks) //等待所有工作完成
wg.Wait()
} //worker 作为goroutine的启动来处理
//从有缓冲的通道传入的工作
func worker(tasks chan string,worker int){
//通知函数已经返回、
defer wg.Done() for{
//等待分配工作
task,ok:= <- tasks
if !ok{
//这里意味着通道已经空了,并且已经被关闭
fmt.Printf("worker;%d:shutting down\n",worker)
return
} //显示我们开始工作
fmt.Printf("worker :%d:start %s\n",worker,task) //随机等待一段时间模拟工作
sleep := rand.Int63n()
time.Sleep(time.Duration(sleep)*time.Microsecond) //显示我们完成了工作
fmt.Printf("worker:%d:completed %s\n",worker,task)
} }
GOROOT=D:\go #gosetup
GOPATH=D:\gospaces #gosetup
D:\go\bin\go.exe build -o C:\Users\Administrator\AppData\Local\Temp\___go_build_listen20_go.exe D:/gocode/test/listen20.go #gosetup
"D:\soft\goland2018.3\GoLand 2018.3.5\bin\runnerw64.exe" C:\Users\Administrator\AppData\Local\Temp\___go_build_listen20_go.exe #gosetup
worker ::start task :
worker ::start task :
worker ::start task :
worker ::start task :
worker::completed task :
worker ::start task :
worker::completed task :
worker ::start task :
worker::completed task :
worker ::start task :
worker::completed task :
worker ::start task :
worker::completed task :
worker::completed task :
worker ::start task :
worker::completed task :
worker;:shutting down
worker ::start task :
worker::completed task :
worker;:shutting down
worker::completed task :
worker;:shutting down
worker::completed task :
worker;:shutting down Process finished with exit code

网易:

package main

import (
"fmt"
) func main() {
var c chan int
fmt.Printf("c=%v\n", c) c = make(chan int, )
fmt.Printf("c=%v\n", c)
c <- /*
data := <-c
fmt.Printf("data:%v\n", data)
*/
<-c
}

nobufChan 不带缓冲(不带大小的chan 无法插入数据的,只有当有人在获取数据时候才可以放入数据)

比如:收快递:只有快递员见到你本人后,只能寄快递

package main

import (
"fmt"
"time"
) func produce(c chan int) {
c <-
fmt.Println("produce finished")
} func consume(c chan int) {
data := <-c
fmt.Println(data)
} func main() {
var c chan int
fmt.Printf("c=%v\n", c) c = make(chan int)
go produce(c)
go consume(c)
time.Sleep(time.Second * )
}

goroutine_sync 模拟sleep阻塞的功能

package main

import (
"fmt"
"time"
) func hello(c chan bool) {
time.Sleep( * time.Second)
fmt.Println("hello goroutine") c <- true
} func main() {
var exitChan chan bool
exitChan = make(chan bool)
go hello(exitChan)
fmt.Println("main thread terminate")
<-exitChan
}

只读 只写的chan

package main

import "fmt"

func sendData(sendch chan<- int) {
sendch <-
//<-sendch
} func readData(sendch <-chan int) {
//sendch <- 10
data := <-sendch
fmt.Println(data)
} func main() {
chnl := make(chan int)
go sendData(chnl)
readData(chnl)
}

判断管道是否关闭

package main

import (
"fmt"
) func producer(chnl chan int) {
for i := ; i < ; i++ {
chnl <- i
}
close(chnl)
} func main() {
ch := make(chan int)
go producer(ch)
for {
v, ok := <-ch
if ok == false {
fmt.Println("chan is closed")
break
}
fmt.Println("Received ", v)
}
}

for-range-chan  不需要关注管道是否关闭 管道关闭后 自动退出循环

package main

import (
"fmt"
"time"
) func producer(chnl chan int) {
for i := 0; i < 10; i++ {
chnl <- i
time.Sleep(time.Second)
}
close(chnl)
} func main() {
ch := make(chan int)
go producer(ch)
for v := range ch {
fmt.Println("receive:", v)
}
}

  

待缓冲的chan(容量)

特点:当没有往chan放入数据,直接去获取数据就会报错(死锁);当超过chan容量后,继续放入数据也会报错(死锁)

package main

import "fmt"

func main() {
ch := make(chan string, )
var s string
//s = <-ch
ch <- "hello"
ch <- "world"
ch <- "!"
//ch <- "test"
s1 := <-ch
s2 := <-ch fmt.Println(s, s1, s2)
}

待缓冲的chan

package main

import (
"fmt"
"time"
) func write(ch chan int) {
for i := ; i < ; i++ {
ch <- i
fmt.Println("successfully wrote", i, "to ch")
}
close(ch)
}
func main() {
ch := make(chan int, )
go write(ch)
time.Sleep( * time.Second)
for v := range ch {
fmt.Println("read value", v, "from ch")
time.Sleep( * time.Second)
}
}

长度和容量

package main

import (
"fmt"
) func main() {
ch := make(chan string, )
ch <- "naveen"
ch <- "paul"
fmt.Println("capacity is", cap(ch))
fmt.Println("length is", len(ch))
fmt.Println("read value", <-ch)
fmt.Println("new length is", len(ch))
}

如何等待一组goroutine结束?

方法1:low版本

package main

import (
"fmt"
"time"
) func process(i int, ch chan bool) {
fmt.Println("started Goroutine ", i)
time.Sleep( * time.Second)
fmt.Printf("Goroutine %d ended\n", i)
ch <- true
}
func main() {
no :=
exitChan := make(chan bool, no)
for i := ; i < no; i++ {
go process(i, exitChan)
}
for i := ; i < no; i++ {
<-exitChan
}
fmt.Println("All go routines finished executing")
}

方法2:sync.WaitGroup

package main

import (
"fmt"
"sync"
"time"
) func process(i int, wg *sync.WaitGroup) {
fmt.Println("started Goroutine ", i)
time.Sleep( * time.Second)
fmt.Printf("Goroutine %d ended\n", i)
wg.Done()
}
func main() {
no :=
var wg sync.WaitGroup
wg.Wait()
fmt.Println("wait return")
for i := ; i < no; i++ {
wg.Add()
go process(i, &wg)
}
wg.Wait()
fmt.Println("All go routines finished executing")
}

workerpool的实现

woker池的实现

a,生产者,消费者模型,简单有效

b,控制goroutine的数量,防止goroutine泄露和暴涨

c,基于goroutine和chan,构建wokerpool非常简单

1,任务抽象程一个个job

2,使用job队列和result队列

3,开一个组goroutine进行实际任务计算,并把结果放回result队列

案例:

package main

import (
"fmt"
"math/rand"
) type Job struct {
Number int
Id int
} type Result struct {
job *Job
sum int
} func calc(job *Job, result chan *Result) {
var sum int
number := job.Number
for number != {
tmp := number %
sum += tmp
number /=
} r := &Result{
job: job,
sum: sum,
} result <- r
} func Worker(jobChan chan *Job, resultChan chan *Result) { for job := range jobChan {
calc(job, resultChan)
}
} func startWorkerPool(num int, jobChan chan *Job, resultChan chan *Result) { for i := ; i < num; i++ {
go Worker(jobChan, resultChan)
}
} func printResult(resultChan chan *Result) {
for result := range resultChan {
fmt.Printf("job id:%v number:%v result:%d\n", result.job.Id, result.job.Number, result.sum)
}
} func main() { jobChan := make(chan *Job, )
resultChan := make(chan *Result, ) startWorkerPool(, jobChan, resultChan) go printResult(resultChan)
var id int
for {
id++
number := rand.Int()
job := &Job{
Id: id,
Number: number,
} jobChan <- job
}
}

select

package main

import (
"fmt"
"time"
) func server1(ch chan string) {
time.Sleep(time.Second * )
ch <- "response from server1"
} func server2(ch chan string) {
time.Sleep(time.Second * )
ch <- "response from server2"
} func main() {
output1 := make(chan string)
output2 := make(chan string) go server1(output1)
go server2(output2)
/*
s1 := <-output1
fmt.Println("s1:", s1)
s2 := <-output2
fmt.Println("s2:", s2)
*/ select {
case s1 := <-output1:
fmt.Println("s1:", s1)
case s2 := <-output2:
fmt.Println("s2:", s2)
default:
fmt.Println("run default")
}
}
package main

import (
"fmt"
"time"
) func write(ch chan string) {
for {
select {
case ch <- "hello":
fmt.Println("write succ")
default:
fmt.Println("channel is full")
}
time.Sleep(time.Millisecond * )
}
} func main() {
//select {} output1 := make(chan string, ) go write(output1)
for s := range output1 {
fmt.Println("recv:", s)
time.Sleep(time.Second)
}
}

sync.Mutex

package main

import (
"fmt"
"sync"
) var x int
var wg sync.WaitGroup
var mutex sync.Mutex func add() {
for i := ; i < ; i++ {
mutex.Lock()
x = x +
mutex.Unlock()
}
wg.Done()
} func main() { wg.Add()
go add()
go add() wg.Wait()
fmt.Println("x:", x)
}

package main

import (
"fmt"
"sync"
"time"
) var rwlock sync.RWMutex
var x int
var wg sync.WaitGroup func write() {
rwlock.Lock()
fmt.Println("write lock")
x = x +
time.Sleep( * time.Second)
fmt.Println("write unlock")
rwlock.Unlock()
wg.Done()
} func read(i int) {
fmt.Println("wait for rlock")
rwlock.RLock()
fmt.Printf("goroutine:%d x=%d\n", i, x)
time.Sleep(time.Second)
rwlock.RUnlock()
wg.Done()
} func main() { wg.Add()
go write()
time.Sleep(time.Millisecond * )
for i := ; i < ; i++ {
wg.Add()
go read(i)
} wg.Wait() }

读锁写锁

互斥锁和读写锁比较

package main

import (
"fmt"
"sync"
"time"
) var rwlock sync.RWMutex
var x int
var wg sync.WaitGroup
var mutex sync.Mutex func write() {
for i := ; i < ; i++ {
//rwlock.Lock()
mutex.Lock()
x = x +
time.Sleep( * time.Millisecond)
mutex.Unlock()
//rwlock.Unlock()
}
wg.Done()
} func read(i int) {
for i := ; i < ; i++ {
//rwlock.RLock()
mutex.Lock()
time.Sleep(time.Millisecond)
mutex.Unlock()
//rwlock.RUnlock()
}
wg.Done()
} func main() { start := time.Now().UnixNano()
wg.Add()
go write() for i := ; i < ; i++ {
wg.Add()
go read(i)
} wg.Wait()
end := time.Now().UnixNano()
cost := (end - start) / /
fmt.Println("cost:", cost, "ms")
}

package main

import (
"fmt"
"sync"
"sync/atomic"
"time"
) var x int32
var wg sync.WaitGroup var mutex sync.Mutex func addMutex() {
for i := ; i < ; i++ {
mutex.Lock()
x = x +
mutex.Unlock()
}
wg.Done()
} func add() {
for i := ; i < ; i++ {
//mutex.Lock()
//x = x +1
atomic.AddInt32(&x, )
//mutex.Unlock()
}
wg.Done()
} func main() { start := time.Now().UnixNano()
for i := ; i < ; i++ {
wg.Add()
go add()
//go addMutex()
} wg.Wait()
end := time.Now().UnixNano()
cost := (end - start) / /
fmt.Println("x:", x, "cost:", cost, "ms")
}

atomic

其它案例:

先看代码

package main
import (
"strings"
"fmt"
"time"
) func main() { users:=strings.Split("shenyi,zhangsan,lisi,wangwu",",")
ages:=strings.Split("19,21,25,26",",") c1,c2:=make(chan bool),make(chan bool)
ret:=make([]string,)
go func() {
for _,v:=range users{
<-c1
ret=append(ret,v)
time.Sleep(time.Second)
c2<-true
}
}()
go func() {
for _,v:=range ages{
<-c2
ret=append(ret,v)
c1<-true
}
}()
c1<-true
fmt.Println(ret) }

打印:

[shenyi]

package main
import (
//_ "github.com/go-sql-driver/mysql"
"io/ioutil"
"net/http"
"fmt"
) func main() { url:="https://news.cnblogs.com/n/page/%d/" c:=make(chan map[int][]byte)
for i:=;i<=;i++{
go func(index int) {
url:=fmt.Sprintf(url,index)
res,_:=http.Get(url)
cnt,_:= ioutil.ReadAll(res.Body)
c<-map[int][]byte{index:cnt} if index== {
close(c)
}
}(i)
} for getcnt:=range c{
for k,v:=range getcnt{
ioutil.WriteFile(fmt.Sprintf("./files/%d",k),v,)
} } }

打印:

。。。。会一直hang住

channel补充的更多相关文章

  1. 《Single Image Haze Removal Using Dark Channel Prior》一文中图像去雾算法的原理、实现、效果(速度可实时)

    最新的效果见 :http://video.sina.com.cn/v/b/124538950-1254492273.html 可处理视频的示例:视频去雾效果 在图像去雾这个领域,几乎没有人不知道< ...

  2. paper 105: 《Single Image Haze Removal Using Dark Channel Prior》一文中图像去雾算法的原理、实现、效果及其他

    在图像去雾这个领域,几乎没有人不知道<Single Image Haze Removal Using Dark Channel Prior>这篇文章,该文是2009年CVPR最佳论文.作者 ...

  3. redis补充和rabbitmq讲解

    线程池: redis发布订阅: rabbitMQ: MySQL; python pymysql: python orm SQLAchemy: paramiko: 堡垒机: 1.线程池 1.1 cont ...

  4. 文成小盆友python-num12 Redis发布与订阅补充,python操作rabbitMQ

    本篇主要内容: redis发布与订阅补充 python操作rabbitMQ 一,redis 发布与订阅补充 如下一个简单的监控模型,通过这个模式所有的收听者都能收听到一份数据. 用代码来实现一个red ...

  5. Java NIO (三) 通道(Channel)

    通道(Channel):由 java.nio.channels 包定义的,Channel 表示 IO 源与目标打开的连接.Channel 类似于传统的"流",只不过 Channel ...

  6. Go基础--goroutine和channel

    goroutine 在go语言中,每一个并发的执行单元叫做一个goroutine 这里说到并发,所以先解释一下并发和并行的概念: 并发:逻辑上具备同时处理多个任务的能力 并行:物理上在同一时刻执行多个 ...

  7. Java 200+ 面试题补充② Netty 模块

    让我们每天都能看到自己的进步.老王带你打造最全的 Java 面试清单,认真把一件事做到最好. 本文是前文<Java 最常见的 200+ 面试题>的第二个补充模块,第一模块为:<Jav ...

  8. Redis基础知识补充及持久化、备份介绍(二)--技术流ken

    Redis知识补充 在上一篇博客<Redis基础认识及常用命令使用(一)--技术流ken>中已经介绍了redis的一些基础知识,以及常用命令的使用,本篇博客将补充一些基础知识以及redis ...

  9. Centos7.4安装配置haproxy和Keepalived补充内容

    补充比较杂 1.当master服务恢复正常之后,backup机器收到消息,然后让出vip 下面是master机器服务恢复正常后,backup机器的Keepalived日志 收到master的消息通知, ...

随机推荐

  1. ES6之主要知识点(七)对象

    1.属性的简洁表示法 ES6 允许直接写入变量和函数,作为对象的属性和方法.这样的书写更加简洁. function f(x, y) { return {x, y}; } // 等同于 function ...

  2. osg::readPixels,glreadPixels截图,保存图片的alpha不对,总是255(1)

    这个函数最近折磨了我很久很久,因为需要用osg截图保存到本地,但是这个图片要具有alpha值,也就是背景的alpha值全为0,但是在公司上用_image->readPixels(448, 28, ...

  3. 2019-8-31-C#-性能分析-反射-VS-配置文件-VS-预编译

    title author date CreateTime categories C# 性能分析 反射 VS 配置文件 VS 预编译 lindexi 2019-08-31 16:55:58 +0800 ...

  4. leetcode 238 & leetcode 152 & leetcode 228

    lc238 Product of Array Except Self 遍历两次数组 用一个res[] 记录答案 1) 第一次,从左往右遍历 res[i] 记录0~i-1的乘积 2) 第二次,从右往左遍 ...

  5. 廖雪峰Java10加密与安全-4加密算法-1对称加密算法

    1.对称加密算法 加密和解密使用同一个密钥,例如WinRAR. WinRAR在对文件进行打包的时候,可以设置一个密码,在解压的时候需要使用同样的密码才能正确的解压. 加密:encrypt(key,me ...

  6. 初识类(class&struct)及C/C++封装的差异

    初识类(class&struct) 面向对象三大特性:封装.继承和多态.其中不得不谈的就是类,通过类创建一个对象的过程叫实例化,实例化后使用对象可以调用类成员函数和成员变量,其中类成员函数称为 ...

  7. JS对象和数组深浅拷贝总结②

    在实际开发中遇到过太多次深拷贝浅拷贝的问题.总结一下~ JS数据存储和深浅拷贝实际运用① 这是之前写过的一篇文章,解决浅拷贝深拷贝的问题只说了一种方法,今天来补充一下. 介绍深拷贝和浅拷贝都在上一篇文 ...

  8. python学习笔记2_二元运算符和比较运算

    一.二元操作符 a+b : a加b a-b :  a减b a*b :  a乘b a/b : a除以b a//b:a整除以b,表示的是返回a除以b的结果的整数部分,而不是证明了a能被b整除.要证明a能被 ...

  9. HBase性能优化方法总结 (转)

    AutoFlush 通过调用HTable.setAutoFlushTo(false)方法可以将HTable写客户端自动flush关闭,这样可以批量写入数据到HBase,而不是有一条put就执行一次更新 ...

  10. 2019-7-29-Roslyn-使用-Target-替换占位符方式生成-nuget-打包

    title author date CreateTime categories Roslyn 使用 Target 替换占位符方式生成 nuget 打包 lindexi 2019-7-29 10:1:1 ...