为什么这个 Go 程序会挂起?

Why does this Go program hang?

我正在设置一个由三个 goroutine 组成的链,每个 goroutine 都有一个输入和输出通道。 goroutines 将从输入通道读取直到它关闭,增加值,将它发送到输出通道。但是,下面的程序会因以下输出而死锁:

goroutine 'one': 1
goroutine 'two': 2
goroutine 'three': 3
goroutine 'one': 10
goroutine 'two': 11
goroutine 'one': 100
fatal error: all goroutines are asleep - deadlock!

代码:

package main

import (
  "fmt"
)

func int_channel(id string, i chan int, o chan int) {
  defer close(o)

  for x := range i {
    fmt.Printf("goroutine '%s': %d\n", id, x)
    o <- x + 1
  }

  fmt.Println("done")
}

func main() {
  c0 := make(chan int)
  c1 := make(chan int)
  c2 := make(chan int)
  c3 := make(chan int)

  go int_channel("one", c0, c1)
  go int_channel("two", c1, c2)
  go int_channel("three", c2, c3)

  c0 <- 1
  c0 <- 10
  c0 <- 100
  c0 <- 1000
  c0 <- 10000
  c0 <- 100000
  close(c0)

  fmt.Println("Sent all numbers to c0")

  for x := range c3 {
    fmt.Printf("out: %d\n", x)
  }
}

它挂起是因为从未到达从输出通道读取的循环,因此通道不是 "emptyed" 并且一旦每个通道都有一个值写入其中就无法取得任何进展并且程序挂起。要修复它,请写入另一个 goroutine 的输入,即

func main() {
  c0 := make(chan int)
  c1 := make(chan int)
  c2 := make(chan int)
  c3 := make(chan int)

  go int_channel("one", c0, c1)
  go int_channel("two", c1, c2)
  go int_channel("three", c2, c3)

  go func(){
    c0 <- 1
    c0 <- 10
    c0 <- 100
    c0 <- 1000
    c0 <- 10000
    c0 <- 100000
    fmt.Println("Sent all numbers to c0")
    close(c0)
  }()


  for x := range c3 {
    fmt.Printf("out: %d\n", x)
  }
}

IOW,当行 c0 <- 1 被执行时,值流过所有三个通道并在 c3 中结束,但是由于尚未到达 reader 循环,它只是 "sits in there"。然后执行 c0 <- 10 行,这个值在 c2 中结束,因为它不能写入 c3 - 以前的值仍然在那里,阻止写入。因此,当执行 c0 <- 100 行时,所有通道都已满,无法进行进一步的处理。

您没有及时阅读 c3。您向 c0 发送的值太多了。所以通道之间的通信是这样的:

send 1 on c0 => recv 1 on c1 => send 2 on c2 =>
        recv 2 on c2 => send 3 on c3 => recv 3 on c3

send 10 on c0 => recv 10 on c1 => send 11 on c2 =>
        recv 11 on c2 => send 12 on c3 -- // c3 still hasn't been read
                                          // from and so send
                                          // operation blocks here.

c0、c1 和 c2 继续接收,直到它们的所有发送最终都被阻塞。你可以参考下面@ain的回答来解决。