为什么有些频道操作会阻塞而有些不会

我一直看到一些文章(和 golang 自己的文档)描述通道操作的方式与我在现实中看到的不一致。


它与 Go 如何阻塞通道读/写有关。我现在在多个地方读到,当一个 goroutine 看到对通道的读取或写入时,它会阻止执行,这意味着它要么等待接收数据,要么等待另一个 goroutine 从通道接收数据。


但是,如果您查看以下示例,这显然不是第二次写入发生的情况。


package main


import (

    "fmt"

)


func firstFunc(ch chan string) {

    fmt.Println("firstFunc Hello", <-ch)

    fmt.Println("firstFunc() carries on getting called")

}


func secondFunc(ch chan string) {

    fmt.Println("secondFunc Hello", <-ch)

    fmt.Println("secondFunc() carries on getting called")

}


func main() {

    fmt.Println("main() started")

    c1 := make(chan string)

    c2 := make(chan string)


    go firstFunc(c1)

    go secondFunc(c2)


    c1 <- "John"

    c2 <- "Bob"


    fmt.Println("main() ended")

}

这就是我解释 Go 执行此代码的方式:

  1. 它打印 main() 开始的消息,创建通道 c1 和 c2 并将 firstFunc 和 secondFunc goroutine 排队(但此时不执行它们)

  2. 它到达 c1 <- "John" 并阻止它,直到另一个 goroutine 从该通道读取

  3. 此时它调度 firstFunc 从 c1 读取并继续执行其余代码直到函数结束

  4. main() 再次被重新安排,下一行是 C2 <- "Bob",此时我认为 main() 应该再次阻塞,就像它对 "John" 所做的那样,并等待 secondFunc 在继续之前从中读取。但这不是发生的事情。输出:

main() started

firstFunc Hello John

firstFunc() carries on getting called

main() ended

它不会简单地阻止对“Bob”的写入,而是继续执行直到 main() 完成并且从不调度 secondFunc。


现在这已经成为我学习 Go 的一个障碍,因为我不确定是我不能信任的文章还是我的理解存在差距。


我将不胜感激这方面的一些帮助。


陪伴而非守候
浏览 147回答 3
3回答

红颜莎娜

一个无缓冲的通道写入将阻塞,直到另一个 goroutine 从中读取。发生这种情况时,读取和写入 goroutine 都会启用。之后,无法保证执行将如何进行。在您的示例中,firstFunc从通道读取后,它会运行直到完成。主 goroutine 写入通道 2,启用secondFunc,但在它有机会完成写入之前,主 goroutine 结束。这里的问题是有许多可能的执行顺序,而您碰巧观察到一种特定的顺序。

largeQ

您看不到输出,secondFunc因为 main 在它有机会执行之前就结束了。解决此问题的一种简单方法是time.Sleep(time.Second)在打印“main() 结束”之前添加。更正确的方法是使用等待组,如下所示:wg := sync.WaitGroup{}wg.Add(2)go func() { firstFunc(c1); wg.Done() }()go func() { secondFunc(c2); wg.Done() }()... // write to channelswg.Wait() // This blocks till both the go-routines are done

长风秋雁

它不会因为有 2 个通道侦听器(firstFunc 和 secondFunc)而阻塞。这些以随机顺序阅读您的消息(以先醒来者为准)。所以阻塞是存在的,但对于活跃的听众来说是无法观察到的。您唯一会看到的是,有时您会首先看到 firstFunc 的输出,有时您会看到 secondFunc 的输出
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go