为啥在全局范围内声明通道会产生死锁问题

Posted

技术标签:

【中文标题】为啥在全局范围内声明通道会产生死锁问题【英文标题】:Why declaring a channel in global scope gives deadlock issue为什么在全局范围内声明通道会产生死锁问题 【发布时间】:2021-06-26 09:13:22 【问题描述】:

在 3 个代码 sn-ps 中,一个在本地范围内声明通道的代码有效,其他代码 sn-ps 产生死锁问题,以前的 answered SO question 之一在这里说尽量避免在全局范围内声明通道。我查看了official docs,但没有找到任何解释。

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

    make(chan int)var myChan chan int 除了作用域和初始化方面有何不同?

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

(为简洁起见,在 sn-ps 中省略了 import 和 'package main')

// 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】:

因为通过声明未初始化的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,则发送者和接收者之间没有相互引用;它们都被阻塞在独立频道上等待并且永远不会解除阻塞。

【讨论】:

以上是关于为啥在全局范围内声明通道会产生死锁问题的主要内容,如果未能解决你的问题,请参考以下文章

为啥在同一个 goroutine 中使用无缓冲通道会导致死锁?

为啥函数被描述为块范围

为啥 GLfloat 需要全局范围?

为啥这个事务会产生死锁?

为啥下面的代码会产生死锁

在全局范围内两次声明相同的静态变量