GoLang在接收值之前通道死锁

  • 本文关键字:通道 死锁 GoLang go
  • 更新时间 :
  • 英文 :


我不明白为什么这不起作用 https://play.golang.org/p/_ALPii0pXV6 但这 https://play.golang.org/p/vCOjAr-o54e 有效。

据我了解,goroutine异步发送到值true到a,12发送到b。在主函数中,a 被阻止,直到它收到一个值。为什么当我重新排列它以使 b 在 a 之前被阻塞时,会导致死锁?

Go 通道默认为无缓冲。这意味着在接收器读取通道之前,它无法在通道上发送。这实际上是 Go 首选模式。在大多数情况下,它比缓冲通道更有效。

对于您的第一个代码来说,这意味着 goroutine 在完成对通道 a 的写入之前无法继续写入通道 b。在主 goroutine 读取 a 之前,它无法做到这一点。

Go by Example 解释说,默认情况下,通道发送和接收会一直等到发送例程和接收例程都准备就绪。以下示例使这种阻塞变得明显:

func main() {
    ch := make(chan int)
    ch <- 1
    fmt.Println(<-ch)
}

这段代码导致死锁,因为唯一的goroutine(主要的)卡在ch <- 1,等待另一个goroutine接收。它几乎不知道我们期待它成为下一行的接收器。

这就解释了为什么你的第一个示例不起作用,因为另一个 goroutine 不会在b上发送,直到它在 a 上的发送操作完成。但是主例程在b收到之前不会在a上收到!所以两者都被困在永远等待。

若要阅读有关此类操作(称为同步操作)的详细信息,请查看此说明。

如果你按照顺序

执行的方式重写代码,那么就会更清楚发生了什么。

原始代码:

func main() {
    a := make(chan bool)
    b := make(chan int64)
    go func(a chan bool, b chan int64) {
        fmt.Println("Here1")
        a <- true
        b <- 12
    } (a,b)
    fmt.Println("Here2")
    fmt.Println(fmt.Sprintf("%d", <-b))
    fmt.Println(fmt.Sprintf("%v", <-a))
}

相同代码顺序执行的紧密表示:


    a := make(chan bool)
    b := make(chan int64)
    fmt.Println("Here2") // Prints
    // Pass control to new goroutine
    fmt.Println("Here1")
    a <- true     // Write to channel a and block goroutine here and pass control to main
    fmt.Println(fmt.Sprintf("%d", <-b)) // Tries to read from b but nothing has been written to it so blocks. At this point all your goroutines are blocked hence the deadlock.
    fmt.Println(fmt.Sprintf("%v", <-a)) // doesn't even reach here.
    b <- 12
}

最新更新