Rob Pike 在 Google I/O 2012 - Go Concurrency Patterns 里演示了一个例子(daisy chain)。 视频地址:https://www.youtube.com/watch?v=f6kdp27TYZs

这个例子抽象于“传话游戏”,几个人站成一队,第一个人跟第二个人悄悄说一句话,依次传到最后一个人,看看最后一个人听到的和第一个人说的差别有多大。

代码如下:

package main

import "fmt"

func pass(left, right chan int){
left <- 1 + <- right
} func main(){
const n = 50
leftmost := make(chan int)
right := leftmost
left := leftmost for i := 0; i< n; i++ {
right = make(chan int)
// the chain is constructed from the end
go pass(left, right) // the first goroutine holds (leftmost, new chan)
left = right // the second and following goroutines hold (last right chan, new chan)
}
go func(c chan int){ c <- 1}(right)
fmt.Println("sum:", <- leftmost)
}

这段代码产生了一个单向的管道环,每个节点对输入的值加了1,然后输出给下一个节点,最后到终点 leftmost。重点我认为有以下几个:

1,循环中的 goroutine ;

2,unbuffered channel 的连接和阻塞;

3,goroutine 对 channel 的竞争;

第一点:循环中的 goroutine 其实很像 js 中的循环中的异步请求,或者更直观的,像是循环中的 setTimeout()。对于 main 来说,goroutine 是异步的,是对线程的细粒度抽象,把它当做一个异步任务就可以了。但是包含了 channel 的 goroutine 就有了阻塞的成分。channel 也体现了 Golang 的设计理念之一:Do not communicate by sharing memory; instead, share memory by communicating 。

第二点:unbuffered channel (make(chan int)) 可以看做是非常短的管子,里面连一个字节都不能存储,必须先找到两端的输入和输出,不然就会出问题(阻塞)。比如下面代码:

func main(){
c := make(chan int)
c <- 1
fmt.Println( <- c)
} // fatal error: all goroutines are asleep - deadlock!

上面的代码中, c <- 1 这一行给 channel 输入了数据,但是此时还没有接收者(代码是同步执行的),因此卡死在这儿了。

那如果先给 channel 指定了输出,然后再输入数据呢? 结果是一样的,只有接收者没有输入者,一样卡死。

改成这样就可以了:

func main(){
c := make(chan int)
go func(){fmt.Println(<- c)}()
c <- 1
}

这里先在 goroutine 里指定了 Println 作为接收者,然后给了输入。

可以理解为:channel 不能同时输入和输出, <- c <- 1  会报错(可能 Golang 觉得这样是没有意义的);指定输入和输出必须写在两行,而代码的同步执行决定了不能同时指定输入和输出,因此只能用 goroutine 。实际上 channel 本身也是为了 goroutine 间的通讯。

buffered channel 就比较好理解,是带有容器的管道,可以存储一定数量的数据。但是当容器满的时候,表现就和 unbuffered channel 一样,会阻塞。

第三点:第一块代码里产生的管道环是从终点开始连接起的,最后一根管道实际上是数据流的第一节管道。在这个环刚完成的时候,所有管道都是空的,没有输入。这时所有的 goroutine 都被阻塞了,倒数第三行的 go 给了第一个管道一个输入,于是这点数据就流到了最后。

最新文章

  1. for in 结构
  2. Android中TextView setText int 报错
  3. SQLSERVER2014 2014年4月1日发布
  4. unity3d 本地数据存储
  5. IDEA中如何使用Maven进行打包。 IDEA版本是14
  6. sql 语句累积
  7. mongodb教程
  8. GUI &amp; Event例子
  9. quartz.net持久化和集群
  10. 取缔Chrome装载电脑管家的广告过滤脚本代码
  11. Django通用视图执行过程
  12. yii2邮件配置教程,报Expected response code 250 but got code &quot;553&quot;原因
  13. vim 的各种用法,很实用哦,都是本人是在工作中学习和总结的
  14. WordPress制作圆形头像友情链接页面的方法
  15. springboot2系列目录
  16. Sql Server数据库之约束
  17. 找到多个与名为“Home”的控制器匹配的类型的解决方案
  18. 【译】第14节---数据注解-MaxLength/MinLength
  19. python中for循环的底层实现机制 迭代
  20. ip定位

热门文章

  1. c++ 利用new动态的定义二维数组
  2. LeetCode——Single Number
  3. webpack中,require的五种用法
  4. CentOS下PostgreSQL的安装与配置
  5. centos7.2启动级别
  6. 收集一些常用的CDN链接!无需下载快速使用!
  7. RMAN概述及其体系结构
  8. 模拟退火算法A Star not a Tree?(poj2420)
  9. redis cluster集群部署
  10. 设计模式之&mdash;&mdash;Decorator模式