刚开始学习golang,并没有完全理解死锁是如何产生的。这是一个改编自 golang playground 教程的示例:
package main
import "fmt"
func fibonacci(c, quit chan int) {
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case q:= <-quit:
fmt.Println(q)
return
}
}
}
func pp(c chan int, quit chan int){
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}
func main() {
c := make(chan int)
quit := make(chan int)
// here it's good, no deadlock
go pp(c,quit)
fibonacci(c, quit)
// but if change the order of above two line:
// fibonacci(c,quit)
// go pp(c,quit)
// it will deadlock
}
为什么上面两行的顺序很重要?
最佳答案
您有两个函数,它们需要同时运行才能使 channel 通信正常工作 - 一个必须同时接收另一个发送。在这种情况下:
go pp(c,quit)
fibonacci(c, quit)
您将 pp
作为 goroutine 启动,它开始运行,然后您调用 fibonacci
,这样两者都在运行,并且一切正常。如果按照您的建议将其更改为:
fibonacci(c, quit)
go pp(c,quit)
然后您将 fibonacci
作为常规函数调用,而不是作为 goroutine,这意味着在 fibonacci
返回之前不会执行下一行。因为 fibonacci
期望从它的 channel 接收到一些东西,它会阻塞直到那发生——这永远不会发生,因为没有任何东西同时从它读取。因此你的僵局。
问题不是函数的顺序,也不是 channel 缓冲——问题是如果你想同时运行两个函数,你先调用的那个必须作为goroutine运行(或两者):
go fibonacci(c, quit)
pp(c,quit)
可以正常工作,因为它同时调用 fibonacci
,然后调用可以同时运行的 pp
。你可以在这里看到它的实际效果:https://play.golang.org/p/4o3T0z5n40X
如果您使用的是 WaitGroup
,您甚至可以将它们作为 goroutine 运行,并且它们会同时运行:
go fibonacci(c, quit, wg)
go pp(c,quit, wg)
尽管在您的情况下这不是必需的并且增加了复杂性。
关于去 channel 死锁问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53095585/