【问题标题】:When does a channel block a goroutine通道何时阻塞 goroutine
【发布时间】:2019-11-04 11:44:54
【问题描述】:

如果我定义了一个没有缓冲区的通道并将一个数据写入其中,它会立即阻塞(以便内核将寻找另一个从通道读取的未阻塞 goroutine),还是继续执行并在下一次时阻塞某些代码在尚未读取时尝试再次写入通道?

以下是我为研究这个问题而写的两段代码。

代码1:

package main

import "fmt"

func main() {
    c := make(chan int)
    go func() {
        for i := 0;i < 3; i++ {
            c <- i
            fmt.Printf("number %v inserted into channel\n", i)
        }
    }()
    for i := 0; i < 3; i++ {
        fmt.Printf("number poped from channel %v\n", <-c)
    }
}

输出是这样的:

number 0 inserted into channel
number poped from channel 0
number poped from channel 1
number 1 inserted into channel
number 2 inserted into channel
number poped from channel 2

在第一次写入 c 后,这个 goroutine 似乎继续执行,因为打印了“数字 0 插入通道”。

代码2:

package main

import "fmt"

func main() {
    c := make(chan int)
    c <- 2
    fmt.Println("Something is written into channel")
    <-c
}

由于运行时报死锁错误,这段代码无法正常运行。

fatal error: all goroutines are asleep - deadlock!

在我看来,当执行 c

总之,在第一段代码中,我得出结论,写入通道不会立即阻塞 goroutine,而是从第二段代码开始。我在哪里弄错了,通道何时阻塞了 goroutine?

【问题讨论】:

  • 此外,IMO 的源代码可读性极强(因为它正在运行!!!:))golang.org/src/runtime/chan.go#L142
  • 将接收分解为单独的变量j := &lt;-c 可能有助于您理解(编译器很可能会自行执行此操作)。传递值后,fmt.Printf 调用中的一个或两个都可以继续进行,并且不能保证哪个会先发生。

标签: go goroutine


【解决方案1】:

发送到没有可用缓冲区空间的通道会阻塞发送方,直到发送完成;从没有可用消息的通道接收会阻塞接收器,直到接收可以完成。一个无缓冲的通道永远不会有缓冲空间——发送块直到接收到东西,反之亦然。这在围棋之旅中有所介绍:https://tour.golang.org/concurrency/2

请记住,您的代码是并发的,因此您不能过多地阅读输出语句的顺序。一个 goroutine 可以在发送/接收操作和你的消息打印到标准输出之间进入睡眠状态。

【讨论】:

  • 感谢您的说明。我试图分析操作的顺序,但完全忘记了它可能是任何东西,因为它们是并发的。经验证,OP 从第一个代码中得到的输出肯定是可能的输出之一。
猜你喜欢
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
  • 2021-05-03
  • 2014-07-19
  • 2020-04-03
  • 2020-10-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多