Golang 入站通道未在 goroutine 内接收

请帮助我理解为什么<-done在这种情况下没有接收入站频道?


func main() {

    done := make(chan bool)

    println("enter")

    defer func() {

        println("exit")

    }()

    defer func() {

        println("  notify start")

        done <- true

        println("  notify end")

    }()     

    go func() {

        println("    wait start")

        <-done

        println("    wait end")

    }()

    time.Sleep(time.Millisecond) // << when this is removed, it works.

}

我期待输出是:


enter

  notify start

    wait start

    wait end

  notify end

exit

但它是:


enter

    wait start

  notify start

  notify end

exit

我最初假设done通道以某种方式被提前关闭或清理,但即使done是全局的,它也会导致相同的意外行为。


不应该<-done阻塞直到done <- true发生?反之亦然?


解决

似乎我期望程序在退出之前等待所有 goroutine 完成。这是一个错误的假设。


这是一个肮脏的解决方法:


func main() {

    done, reallydone := make(chan bool), make(chan bool)

    println("enter")

    defer func() {

        <-reallydone

        println("exit")

    }()

    go func() {

        println("    wait start")

        <-done

        println("    wait end")

        reallydone <- true

    }()

    defer func() {

        println("  notify start")

        done <- true

        println("  notify end")

    }()

    time.Sleep(time.Millisecond)

}


郎朗坤
浏览 238回答 2
2回答

梵蒂冈之花

当您使用 sleep 时,它为 goroutine 提供时间启动,然后当它从通道读取时, main 在最后一个println("&nbsp; &nbsp; wait end")被调用之前退出。但是,如果您不调用 sleep,defer 将阻塞,直到 goroutine 从中读取并为其提供足够的时间进行打印。如果您将代码移动到不同的函数并从 main 调用它,它将按预期工作。func stuff() {&nbsp; &nbsp; done := make(chan bool)&nbsp; &nbsp; println("enter")&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; println("exit")&nbsp; &nbsp; }()&nbsp; &nbsp; go func() {&nbsp; &nbsp; &nbsp; &nbsp; println("&nbsp; &nbsp; wait start")&nbsp; &nbsp; &nbsp; &nbsp; <-done&nbsp; &nbsp; &nbsp; &nbsp; println("&nbsp; &nbsp; wait end")&nbsp; &nbsp; }()&nbsp; &nbsp; defer func() {&nbsp; &nbsp; &nbsp; &nbsp; println("&nbsp; notify start")&nbsp; &nbsp; &nbsp; &nbsp; done <- true&nbsp; &nbsp; &nbsp; &nbsp; println("&nbsp; notify end")&nbsp; &nbsp; }()}func main() {&nbsp; &nbsp; stuff()}

慕容708150

done通道上的事件序列:“[当] 通道无缓冲时,只有当发送方和接收方都准备好时,通信才会成功。” 对于你的例子,的done信道是无缓冲:main。done := make(chan bool)接收等待发送:go func()。<-done&nbsp;接收就绪 ( <-done),发送:defer func()。done <- true该main函数结束,不等待够程(go func())结束。输出:enter&nbsp; &nbsp; wait start&nbsp; notify start&nbsp; notify endexitGo 编程语言规范渠道类型通道为并发执行函数提供了一种机制,通过发送和接收指定元素类型的值来进行通信。未初始化通道的值为 nil。可以使用内置函数 make 创建一个新的初始化通道值,该函数将通道类型和可选容量作为参数:make(chan&nbsp;int,&nbsp;100)容量(以元素数为单位)设置通道中缓冲区的大小。如果容量为零或不存在,则通道没有缓冲,只有当发送方和接收方都准备好时,通信才能成功。否则,如果缓冲区未满(发送)或非空(接收),则通道被缓冲并且通信成功而不会阻塞。一个 nil 通道永远不会准备好进行通信。Go 语句程序执行首先初始化主包,然后调用函数 main。当该函数调用返回时,程序退出。它不会等待其他(非主)goroutine 完成。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go