首页 > 解决方案 > 通道何时阻塞 goroutine

问题描述

如果我定义了一个没有缓冲区的通道并将一个数据写入其中,它会立即阻塞(以便内核将寻找另一个从通道读取的未阻塞 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 <-2 被执行时,goroutine 被阻塞(如果它没有被阻塞,fmt.Println 行将被执行,继续执行将在 <-c 处解锁 c)。当这个 goroutine 被阻塞时,内核会搜索其他 goroutine 并没有找到,所以它会报告死锁错误。

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

标签: gogoroutine

解决方案


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

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


推荐阅读