这是 V2EX 从很久以前的一道面试题我直接拿来练手的,但是我并不理解死锁出现和不出现的原因
使用两个 goroutine 交替打印序列,一个 goroutinue 打印数字, 另外一个 goroutine 打印字母, 最终效果如下 12AB34CD56EF78GH910IJ 。
版本一,这个代码是能正常运行的,但结尾要通过 sleep 来阻塞
ch1 := make(chan bool) ch2 := make(chan bool) go func() { // for loop to print 2 to 10, increase by 2 for i := 2; i <= 10; i += 2 { <-ch1 fmt.Print(i - 1) fmt.Print(i) ch2 <- true } close(ch2) }() go func() { str := "abcdefghij" // for loop to print a to j for i := 1; i < len(str); i += 2 { <-ch2 fmt.Print(str[i-1 : i+1]) ch1 <- true } close(ch1) }() ch1 <- true time.Sleep(10 * time.Second)
版本二,通过 done channel ,但这个版本就会出现死锁
ch1 := make(chan bool) ch2 := make(chan bool) done := make(chan bool) // 新增 done go func() { // for loop to print 2 to 10, increase by 2 for i := 2; i <= 10; i += 2 { <-ch1 fmt.Print(i - 1) fmt.Print(i) ch2 <- true } close(ch2) }() go func() { str := "abcdefghij" // for loop to print a to j for i := 1; i < len(str); i += 2 { <-ch2 fmt.Print(str[i-1 : i+1]) ch1 <- true } done <- true // 通知完成 close(ch1) // ch1 关不关闭都会死锁 }() ch1 <- true <-done // 等待完成通知
版本三,基于二调整,不死锁
ch1 := make(chan bool, 1) // 和版本二的核心区别 ch2 := make(chan bool) done := make(chan bool) go func() { // for loop to print 2 to 10, increase by 2 for i := 2; i <= 10; i += 2 { <-ch1 fmt.Print(i - 1) fmt.Print(i) ch2 <- true } close(ch2) }() go func() { str := "abcdefghij" // for loop to print a to j for i := 1; i < len(str); i += 2 { <-ch2 fmt.Print(str[i-1 : i+1]) ch1 <- true } done <- true close(ch1) }() ch1 <- true <-done
我自己的理解是, 版本二死锁的原因,ch1 是 unbuffered channel ,因为第二个协程循环最后,往 ch1 写入后没有取去,导致阻塞死锁
所以在版本三,把 ch1 改成 buffered channel 后,即使没有人取,也不会阻塞
但为什么同样是 unbuffered channel
的 ch1, 在版本一中却不会死锁呢?
另外 <-done
在主线程中扮演怎样的角色
1 klesh 347 天前 总数不对。 版本一是各写了 5 次,没问题。不死锁 版本二 ch1 给写了 6 次,比 ch2 多了一次,最后一个写不进去了。死锁 版本三 ch1 buffer 了,可以写 6 次。不死锁 |
2 klesh 347 天前 done 的意义就是保证所有子线程完成吧?毕竟要提前定好 sleep 多久合适还是挺难的。 |
4 pandaex 347 天前 via Android @timsims 第一个 sleep 约等于 waitwithtimeout 等待两个协程结束,超时了它就拉倒跑路,第二个 done 就死等,一定要等到子携程退出 |
![]() | 6 twig 347 天前 via iPhone 是不是有个东西叫 WaitGroup 跟这个有关系吗?(我也入门,不太清楚。) |