非缓冲通道上的死锁

deadlock on not buffered channel

我目前正在学习 tour of go 教程并进入频道部分,因为我正在做一些测试,我发现了一个我很难理解的奇怪行为

以下代码产生死锁错误

package main

import "fmt"

func main() {
    c := make(chan string)
    c <- "test"
    fmt.Printf("%v", <- c)
}

但执行以下操作之一可以修复代码

使用缓冲通道:

package main

import "fmt"

func main() {
    c := make(chan string, 1)
    c <- "test"
    fmt.Printf("%v", <- c)
}

或将值设置为不同线程上的通道

package main

import "fmt"

func main() {
    c := make(chan string)
    go func(){c <- "test"}()
    fmt.Printf("%v", <- c)
}

第一版代码产生死锁的根本原因是什么?

只有当有另一个 goroutine 从该通道读取数据时,写入无缓冲通道才会成功。在第一种情况下,你只有一个 goroutine,主 goroutine,它写入一个无缓冲的通道,没有其他 goroutine 可以从它读取,所以这是一个死锁。

第二个可以,因为channel有缓冲,填充buffer写入成功。没有读取的第二次写入将死锁。

第三个可行,因为写入发生在一个单独的 goroutine 中,它会等到第一个 goroutine 中的读取运行。

由于通道没有缓冲区,c <- "test" 将阻塞直到从 c 读取数据。由于 reader 在写入之后出现,因此它永远不会达到读取和死锁。

如果通道有缓冲区,c <- "test" 写入缓冲区而不必等待 reader。 reader 然后从通道缓冲区中读取。

这是因为 reader 和 writer 在同一个 goroutine 中,所以必须一个接一个地执行语句。如果 reader 和 writer 在不同的 goroutine 中,writer goroutine 可以阻塞直到 reader goroutine 读取。因此,通常不需要缓冲区。