为了账号安全,请及时绑定邮箱和手机立即绑定

通道阻塞顺序

通道阻塞顺序

Go
回首忆惘然 2023-06-19 17:22:16
我想了解频道在 golang 中的工作方式。我的代码非常简单,但输出却令人惊讶。正如文档所述:从通道读取和写入通道会阻塞当前的 goroutine,因此我认为写入通道会阻塞通道,直到主例程产生为止。package mainfunc rtn(messages chan<- string) {    defer close(messages)    println("p1")    messages <- "ping1"    //for i := 0; i < 10000000; i++ { }    println("p2")    messages <- "ping2"}func main() {    messages := make(chan string)    go rtn(messages)    for msg := range messages {        println(msg)    }}我以为它会打印p1ping1p2ping2但它实际上打印p1p2ping1ping2
查看完整描述

1 回答

?
森林海

TA贡献2011条经验 获得超2个赞

您正在使用一个无缓冲的通道,它作为主 goroutine 和第二个 goroutine 之间的同步点。


在这种情况下,你只知道当第二个 goroutine 在这里时,messages <- "ping1"主要的 goroutine 在 line 上for msg := range messages。因此,不能保证主循环println(msg)立即到达。也就是说,与此同时,第二个 goroutine 可以继续前进并到达 lines println("p2")and messages <- "ping2"。


作为一个反例,我添加一个通道只是为了强制打印之间的完全同步。


package main


func rtn(messages chan<- string, syncChan chan struct{}) {

    defer close(messages)


    println("p1")

    messages <- "ping1"


    //Wait for main goroutine to print its message

    <-syncChan


    //for i := 0; i < 10000000; i++ { }


    println("p2")

    messages <- "ping2"


    //Wait for main goroutine to print its message

    <-syncChan

}


func main() {

    messages := make(chan string)

    syncChan := make(chan struct{})

    go rtn(messages, syncChan)


    for msg := range messages {

        println(msg)

        //Notify the second goroutine that is free to go

        syncChan <- struct{}{}

    }

}

打印出您期望的输出:


p1

ping1

p2

ping2

这是另一个产生您正在寻找的输出的示例。在这种情况下,主 goroutine 被time.Sleep(). 这将使第二个 goroutine 在接收器准备好接收之前准备好发送。因此,发送方实际上会阻塞发送操作。


package main


import (

    "time"

)


func rtn(messages chan<- string) {

    defer close(messages)


    println("p1")

    messages <- "ping1"


    //for i := 0; i < 10000000; i++ { }


    println("p2")

    messages <- "ping2"

}


func main() {

    messages := make(chan string)

    go rtn(messages)


    //Put main goroutine to sleep. This will make the

    //sender goroutine ready before the receiver. 

    //Therefore it will have to actually block!

    time.Sleep(time.Millisecond * 500)


    for msg := range messages {

        println(msg)

    }

}


查看完整回答
反对 回复 2023-06-19
  • 1 回答
  • 0 关注
  • 123 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信