Channel,这次换个学法
Channel,这次换个学法
1 | c := make(chan string) |
1行 新建无缓冲通道
2行 向通道里输入数据
3行 从通道里拿出数据
5行 新建有缓冲通道
有缓冲通道类似快递柜,只要有空间,别人就可以投递,而无缓冲通道则只能双方都在场的情况下,才能让数据成功收发。
下面看一下通道的源代码runtime/chan.go
1 | type hchan struct { |
1行 channel的结构体
2行 环形队列的总长度
3行 环形队列的数据大小
4行 指向datasiz的数组元素的指针
5行 元素大小
6行 是否关闭
7行 元素类型
8行 发送队列索引
9行 接收队列索引
10行 接收队列
11行 发送队列
12行 锁,保护所有hchan结构体的字段,也就是说任何协程想操作通道中的字段,都要拿到这把锁。
为什么是环形队列
先进先出,队列本身的特性就保证的。
空间重复利用,固定的闭环,初始化即分配好内存空间,入队或出队只要返回内存空间的地址即可,重复利用,避免频繁的内存分配和释放的开销操作。类似,铁打的营盘,流水的兵, 数据都可以轮流放上去,但是地址,已经初始化的时候就固定了。
队列发送数据(runtime/chan.go)
1 | func chansend1(c *hchan, elem unsafe.Pointer) { |
上面这个函数,就是向channel发送数据,编译后调用的,比如我们写的 c <- “面向加薪学习”
1 | func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { |
收货人在家等待,可以直接上门送货
9行, 从接收队列里出队一个
10行 send()发送数据
63行 sendDirect()直接发送
66行 memmove,也就是内存移动,直接把copy过去
60行 goready,唤醒该协程
收货人不在家,直接放快递柜
14行 拿到缓存区一个地址
18行 把数据拷贝给缓存地址即可
19行 维护缓存的索引
收货人不在家,快递柜已满
34行 获取当前的协程
35-46行 包装成sudog
47行 sendq的入队
49行 休眠
队列接收数据
1 | func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { |
从快递小哥手里直接拿快递
30行 获取一个发送队列里的Goroutine
31行 recv()
79行 缓存区为空
84行 直接接收 recvDirect()
116行 内存移动memmove()
110行 唤醒Goroutine
从快递柜里拿快递
87行 从缓冲区拿出来数据
93行 把缓冲区的数据拷贝到要接收的Goroutine里
95行 把接下来要发送的数据再发送到缓冲区里
直接从快递柜拿走快递,无其他人等待
36行 队列里有数据
37行 拿到缓存区的数据
42行 把缓存区的数据给接收的Goroutine
只有收货人,没有快递小哥
59行 拿到当前的Goroutine
60-71行 包装成sudog
72行 recvq入队
74行 休眠
如何学习Go语言微服务,快速步入架构师
添加微信 | 公众号更多内容 |
---|---|