老师,您好!关于channel的困惑,望老师指点迷津
来源:13-9 使用channel容易出现deadlock的原因

慕斯0066757
2022-06-23
老师,您好!
在学习channel这一章节时,学生记住了一条结论:goroutine的生产与消费都需要准备就绪,理解的白话文就是channel像是一个流水的管道,进与出都需要准备好,否则容易产生死锁deadlock。
但在学生后续的练习与思考中,如上面截图的代码
forever:是一个无缓冲的通道
<- forever:没有任何goroutine向管道生产数据,一直阻塞中
但结果上述代码并没有出现死锁deadlock的现象,一直循环打印555
这个现象的产生好像与学生记住的结论有些违背,因此产生了困惑,只有消费,没有生产,也没有产生死锁deadlock,好像学生总结的结论并不是正确的
难道go程序认为我开启了一个goroutine,会有机会向forever生产数据吗?
2回答
-
城中城
2022-06-24
第一 只有存值 和 只有取值也是会存在死锁
如下列代码
package main import ( "fmt" "time" ) func main() { // 有缓存 和 无缓冲最大的区别在于 // 有缓存 可以在 协程未开启前 提前存放一个数据进去 forever := make(chan bool) // 创建 channel 这个 是无缓冲通道 //forever := make(chan bool, 1) // 这个是 有缓冲通道 <-forever // 这里 你取出 channel 中的值 但是因为你没存值 导致 主协程 没有结束 一直阻塞在这里 go func() { // 开启协程 在主协程 没死亡前 不会停止运行 for { fmt.Println("555") time.Sleep(time.Second) //forever <- true # 这个 打开这个注释 当你存入 值后 <-forever 就会接受到值 继续往下走 } }() fmt.Println("777") }
为什么这下就有 死锁现象呢
之前就没有呢 跟你的 开启的子协程有关
因为你 创建的是无缓冲通道 导致执行到此行的时候
<-forever
主协程是必须 阻塞在这里 等待其他协程需要 存储这个值
但是 因为 整个代码 运行到这里的时候 没创建任何一个 子协程 导致 这 行
<-forever
注定没办法 解锁 死锁就产生了
那么为什么 只是简单 把 这行放在 执行子协程 代码 前面 就有问题了呢
是因为 当前只有一个 协程 你把这个协程上锁了 所有协程都是睡眠状态 不就 死锁了啊
将 这个含代码放在 执行 子协程后面为什么没有问题呢
<-forever
是因为 当前有二个线程 你只是把主协程 上锁了 不是所有协程都是睡眠状态 所有可以流畅运行
你可以试一试这个
package main import ( "fmt" "time" ) func main() { // 有缓存 和 无缓冲最大的区别在于 // 有缓存 可以在 协程未开启前 提前存放一个数据进去 forever := make(chan bool) // 创建 channel 这个 是无缓冲通道 //forever := make(chan bool, 1) // 这个是 有缓冲通道 go func() { // 开启协程 在主协程 没死亡前 不会停止运行 for { forever <- true fmt.Println("555") time.Sleep(time.Second) //forever <- true # 这个 打开这个注释 当你存入 值后 <-forever 就会接受到值 继续往下走 } }() forever <- true // 这里 你取出 channel 中的值 但是因为你没存值 导致 主协程 没有结束 一直阻塞在这里 fmt.Println("777") }
请问有没有解开你心中的疑虑呢
112022-06-25 -
城中城
2022-06-24
我看起来 代码没有任何问题
我注释一下你的代码
package main import "fmt" func main() { // 有缓存 和 无缓冲最大的区别在于 // 有缓存 可以在 协程未开启前 提前存放一个数据进去 forever := make(chan bool) // 创建 channel 这个 是无缓冲通道 //forever := make(chan bool, 1) // 这个是 有缓冲通道 go func() { // 开启协程 在主协程 没死亡前 不会停止运行 for { fmt.Println("555") //forever <- true # 这个 打开这个注释 当你存入 值后 <-forever 就会接受到值 继续往下走 } }() <-forever // 这里 你取出 channel 中的值 但是因为你没存值 导致 主协程 没有结束 一直阻塞在这里 fmt.Println("777") }
012022-06-24
相似问题