Go 语言 channel 的阻塞问题
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go 语言 channel 的阻塞问题相关的知识,希望对你有一定的参考价值。
参考技术AHello,大家好,又见面了!上一遍我们将 channel 相关基础以及使用场景。这一篇,还需要再次进阶理解channel 阻塞问题。以下创建一个chan类型为int,cap 为3。
channel 内部其实是一个环形buf数据结构 ,是一种滑动窗口机制,当make完后,就分配在 Heap 上。
上面,向 chan 发送一条“hello”数据:
如果 G1 发送数据超过指定cap时,会出现什么情况?
看下面实例:
以上会出现什么,chan 缓冲区允许大小为1,如果再往chan仍数据,满了就会被阻塞,那么是如何实现阻塞的呢?当 chan 满时,会进入 gopark,此时 G1 进入一个 waiting 状态,然后会创建一个 sudog 对象,其实就sendq队列,把 200放进去。等 buf 不满的时候,再唤醒放入buf里面。
通过如下源码,你会更加清晰:
上面,从 chan 获取数据:
Go 语言核心思想:“Do not communicate by sharing memory; instead, share memory by communicating.” 你可以看看这本书名叫:Effective Go
如果接收者,接收一个空对象,也会发生什么情况?
代码示例 :
也会报错如下:
上面,从 chan 取出数据,可是没有数据了。此时,它会把 接收者 G2 阻塞掉,也是和G1发送者一样,也会执行 gopark 将状态改为 waiting,不一样的点就是。
正常情况下,接收者G2作为取出数据是去 buf 读取数据的,但现在,buf 为空了,此时,接收者G2会将sudog导出来,因为现在G2已经被阻塞了嘛,会把G2给G,然后将 t := <-ch 中变量 t 是在栈上的地址,放进去 elem ,也就是说,只存它的地址指针在sudog里面。
最后, ch <- 200 当G1往 chan 添加200这个数据,正常情况是将数据添加到buf里面,然后唤醒 G2 是吧,而现在是将 G1 的添加200数据直接干到刚才G2阻塞的t这里变量里面。
你会认为,这样真的可以吗?想一想,G2 本来就是已经阻塞了,然后我们直接这么干肯定没有什么毛病,而且效率提高了,不需要再次放入buf再取出,这个过程也是需要时间。不然,不得往chan添加数据需要加锁、拷贝、解锁一序列操作,那肯定就慢了,我想Go语言是为了高效及内存使用率的考虑这样设计的。(注意,一般都是在runtime里面完成,不然会出现象安全问题。)
总结 :
chan 类型的特点:chan 如果为空,receiver 接收数据的时候就会阻塞等待,直到 chan 被关闭或者有新的数据到来。有这种个机制,就可以实现 wait/notify 的设计模式。
相关面试题:
2021-GO语言并发编程
2021-GO语言并发编程
- 1、Go语言并发概述
- 2、GO语言多进程编程
- 3、Go语言并发简略-多线程
- 4、Go语言并发简略-非阻塞IO
- 5、Go语言并发简略-协程
- 6、并发简略-对比并发模型
- 7、Go语言goroutine
- 8、Go语言channel
- 9、Go语言channel的操作
- 10、Go语言channel的应用
- 11、Go语言select
- 12、Go协程调度模型-1
- 13、Go协程调度模型-2
- 14、Go语言同步2-等待组
- 15、Go语言同步3-条件变量
- 16、Go语言同步4-sync包的其他API
- 17、Go语言同步5-原子操作
1、Go语言并发概述
2、GO语言多进程编程
3、Go语言并发简略-多线程
4、Go语言并发简略-非阻塞IO
5、Go语言并发简略-协程
6、并发简略-对比并发模型
7、Go语言goroutine
8、Go语言channel
9、Go语言channel的操作
10、Go语言channel的应用
11、Go语言select
12、Go协程调度模型-1
13、Go协程调度模型-2
14、Go语言同步2-等待组
15、Go语言同步3-条件变量
16、Go语言同步4-sync包的其他API
17、Go语言同步5-原子操作
以上是关于Go 语言 channel 的阻塞问题的主要内容,如果未能解决你的问题,请参考以下文章
Nil Channels Always Block(Go语言中空管道总是阻塞)