为什么在全局范围内声明通道会导致死锁问题

时间:2021-03-30 10:44:23

标签: go concurrency scope deadlock channel

在 3 个代码片段中,在本地范围内声明频道的代码片段有效,其他代码片段会导致死锁问题,其中一个前面的 answered SO question 说尽量避免在全局范围内声明频道。我检查了 official docs,但没有找到任何解释。

  1. 尽管我没有阻止通道发送和接收,但为什么全局范围通道会出错?为什么我在这里遇到死锁问题?

  2. 除了作用域和初始化之外,make(chan int)var myChan chan int 有何不同?

  3. 谁能解释和建议更好的文章/文档/链接/pdf 以在 Go 中有效地使用通道(并实现并发)?

(为简洁起见,片段中省略了导入和“包主”)

// 1. channel declared in global scope
var c chan int
func main(){
    go send()
    fmt.Println(<-c)
}
func send(){
    c <- 42
}
//output: fatal error: all goroutines are asleep - deadlock!

// 2. channel declared in global scope + sending channel to goroutine
var c chan int
func main(){
    go send(c)
    fmt.Println(<-c)
}
func send(c chan int){
    c <- 42
}
//output: fatal error: all goroutines are asleep - deadlock!

// 3. using channel as local scope and sharing it with goroutine
func main(){
    c := make(chan int)
    go send(c)
    fmt.Println(<-c)
}
func send(c chan int){
    c <- 42
}

1 个答案:

答案 0 :(得分:5)

因为通过声明一个未初始化的 var c chan intc 具有其类型的零值,在 chan 的情况下为 nil

如果您确实 run your code,错误消息会显示此信息。两个 goroutine 都在 nil chan 上发送/接收:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive (nil chan)]:
main.main()
    /tmp/sandbox288806111/prog.go:11 +0x5c

goroutine 18 [chan send (nil chan)]:
main.send()
    /tmp/sandbox288806111/prog.go:15 +0x39
created by main.main
    /tmp/sandbox288806111/prog.go:10 +0x39

改为使用 make,您显式初始化变量 c,然后它不是 nil

这与全局作用域本身无关。事实上,如果你正确地初始化变量,例如var c chan int = make(chan int),即使在全局范围内,程序也不会死锁。


附加阅读:channel axioms (Dave Cheney)

<块引用>

如果通道为nil,则发送方和接收方没有相互引用;它们都被阻塞在独立的通道上等待并且永远不会解除阻塞。