go学习笔记-goroutine的好兄弟channel
source link: https://studygolang.com/articles/25994
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
上一篇介绍了 atomic
包以及 互斥锁 mutex
来解决并发竞争状态的问题。这一篇主要来介绍 go
中与 goroutine
经常搭档的好兄弟 channel
channel
不仅可以可以来用消除竞争状态,还可以用于不同的 goroutine
中进行通信,发送与接受数据。chaanel的定义有两种,分为 有缓存 与 无缓冲
创建channel
chan1 := make(chan int) // 创建一个无缓冲的 整形 channel chan2 := make(chan int,2)// 创建一个有缓冲的 整形 channel
上面的代码片段,我们分别创建了一个无缓冲的 channel
与一个有缓冲的 channel
。 channel
的创建是使用 make(chan type,[lenght])
来创建,如果指定了第二个参数 length
表示创建了一个长度为 length
的有缓存 channel
,反之我们称之为无缓冲。
channel的值传递
var number int func main() { chan1 := make(chan int) //创建一个无缓冲的 整形channel go numberAdd(chan1) fmt.Printf("改变之后的number:%d\r\n",<-chan1) //改变之后的number:1 } func numberAdd(c chan int) { number++; c<-number; //往chan写值 }
这里我们创建了一个整形的 channel
在 goroutine
中往 chan
中写值,在main函数中取值。
无缓冲与有缓存通道的区别
- 无缓冲通道:是指在接收前没有能力保存任何值的通道。无缓冲的通道要求发送和接收的
goroutine
同时准备好才能完成发送和接收操作。如果两个goroutine
没有同时准备好,通道会导致先执行发送或接收操作的goroutine
阻塞等待。 - 有缓冲通道:在在被接收前可以接受一个或多个值。有缓冲通道不要求发送与接受的
groutine
同时准备好。只有在通道中没有空间容纳新值的时候,发送动作才会发送阻塞;只有在通道中没有值要接收时,接收动作才会阻塞。 - 区别:无缓冲通道可以保证接收跟发送数据是在同一时间,而有缓存通道则不能保证这一点。
下面来看两个例子
无缓存通道
var wait sync.WaitGroup const needProcessNumber = 3 //需要三次加工 func main() { wait.Add(1) sausage := make(chan int) // 腊肠 go processing(sausage) //开始加工程序 sausage<-1 //开始第一次加工 wait.Wait() } func processing(sausage chan int) { defer wait.Done() for { nowNumber := <-sausage fmt.Printf("第%d次加工开始\r\n",nowNumber) for i:=1; i<=10; i++ { fmt.Printf("%d \r\n",i*10) } fmt.Printf("第%d次加工结束\r\n",nowNumber) if nowNumber==needProcessNumber{ fmt.Printf("新鲜的腊肠出炉了\r\n") close(sausage) return }else { go processing(sausage) //等待下一次加工开始 } nowNumber++ sausage <- nowNumber //这里会加锁直到流程交接结束 } }
这个例子创建了一个 Int
无缓冲通道来表示腊肠,做一个腊肠需要三次加工, main
函数中创建了一个 wait
来等待加工完成。准备一个加工的 goroutine processing
等待第一个杯子准备就绪的信号,当接收到第一个信号时,开始加工,然后等待当前加工完成,如果当前 goroutine
不是第三次加工的 goroutine
,那么准备下一个加工程序开始,进入下一个 goroutine
,直到第三次加工完成。
有缓存通道
var wait sync.WaitGroup const ( maxTask = 10 //最大处理工作数 workerNumber = 3 //当前工人数 ) func main() { wait.Add(workerNumber) //等到所有的work都结束 tasks := make(chan int,maxTask) for workerOnline:=1;workerOnline<=workerNumber;workerOnline++ { go worker(tasks,workerOnline) } //增加十个需要处理的工作 for i:=1;i<=maxTask ; i++ { tasks<-i } close(tasks)//所有工作完成 wait.Wait() } //员工开始工作 func worker(task chan int,workNumber int) { defer wait.Done() for{ taskNumber,ok := <-task if !ok { fmt.Printf("工人:%d 没有工作可以做了\r\n",workNumber) return } fmt.Printf("工人:%d 开始工作,当前任务编号:%d\r\n",workNumber,taskNumber) workTime := rand.Int63n(100) time.Sleep(time.Duration(workTime)*time.Millisecond) fmt.Printf("工人:%d 工作完成,当前任务编号:%d\r\n",workNumber,taskNumber) } }
这里我们声明了一个容量为 10
的有缓冲通道 task
来表示总共有十个任务需要 3
个员工来处理。每个员工是一个 goroutine
来单独完成工作。员工首先准备就绪,然后等待任务的下发。当监听到有任务进入时,开始完成工作,直到监听到 task
通道已经关闭。需要注意的是我们在新增完10个任务时就已经关闭了 channel
,这个时候 goroutine
仍然可以从 channel
取值,直到取到的返回数值是 零值
,如果你这个时候获取了 channel
的标志位,那么会返回一个 false
,所以我们判断 channel
是否关闭应该用这个标志位来判断。
期待一起交流
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK