go channel 的行为是否会根据消息的发送方式而改变?

问题描述 投票:0回答:2

为什么这两段代码执行不同?

  1. 运行以下代码返回
    fatal error: all goroutines are asleep - deadlock!
    错误。
func main() {
    ch := make(chan int)
    ch <- 1
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}
  1. 正确运行以下代码会返回 2 和 3(各占一行)。
    ch := make(chan int)
    go buffer(ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

func buffer(ch chan int) {
    ch <- 2
    ch <- 3
}

#1 仅当 ch 被定义为缓冲通道时才能正确执行。为什么?为什么 ch in buffer() 毫无怨言地接受 2 个值?

go channel goroutine
2个回答
0
投票

对无缓冲通道的发送操作将阻塞,直到另一个 goroutine 从中读取。这就是第一段代码死锁的原因:当你发送到通道时,没有其他 goroutines 从它读取。

在第二种情况下,您有一个 goroutine 向通道发送两个值,并且主 goroutine 在发送的同时从通道读取两次。该通道不接受两次发送,它是一个接一个地进行,第一次发送操作被第一次读取解除阻塞,第二次发送操作被第二次读取解除阻塞。


0
投票

在您的代码示例中,由于只有一个 goroutine(主 goroutine)永远不会到达第 3 和 4 行,因此它有效:

ch := make(chan int)
ch <- 1               // blocks forever - as no one is reading
// fmt.Println(<-ch)
// fmt.Println(<-ch)

通过通道进行的通信旨在跨 goroutines 使用——因为这里只有一个 goroutine,它将永远阻塞。

如您所见,缓冲频道会有所帮助...

ch := make(chan int, 1) // buffer of one

ch <- 1               // works
ch <- 2               // blocks forever

// never reached
// fmt.Println(<-ch)
// fmt.Println(<-ch)

...但是有点做作而且不是很有用。

在你的第二个例子中,这是正确的模式,至少有 2 个 goroutines 合作:

go buffer(ch) // goroutine that writes to the channel

创建一个专门的 writer goroutine,释放主 goroutine 以实时成功读取这些写入中的任何一个:

fmt.Println(<-ch)
fmt.Println(<-ch)
© www.soinside.com 2019 - 2024. All rights reserved.