去 channel 死锁问题

标签 go deadlock channel

刚开始学习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/

相关文章:

multithreading - 在线程之间传递 hashmap 的 channel |陷入循环 | rust

Leverice 中的 Jira channel 状态

Go:在 '|'字符前加反斜杠

go - 如何使用golang库中的回调函数?

go - 无法使用 go get 安装 vet 包

go - 将 Go 结构数据嵌入二进制文件

c# - 为什么移动这条线会导致僵局?

mysql - 从 mysql 中的 query_id 查找查询

c# - 写入 Process.StandardInput 时死锁

sockets - curl Phoenix 套接字