【问题标题】:Golang inbound channel not receiving inside a goroutineGolang入站通道未在goroutine内接收
【发布时间】:2014-10-10 01:15:48
【问题描述】:

请帮助我理解为什么在这种情况下没有接收入站<-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 &lt;- true 出现之前,&lt;-done 不应该阻塞吗?反之亦然?

分辨率

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

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

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)
}

【问题讨论】:

    标签: go channel


    【解决方案1】:

    当你使用 sleep 时,它会给 goroutine 启动时间,然后在它从通道中读取数据时,main 在最后一个 println(" wait end") 被调用之前退出。

    但是,如果你不调用 sleep,defer 将阻塞,直到 goroutine 从中读取并给它足够的时间来打印。

    如果您将代码移动到不同的函数并从 main 调用它,它将按预期工作。

    func stuff() {
        done := make(chan bool)
        println("enter")
        defer func() {
            println("exit")
        }()
        go func() {
            println("    wait start")
            <-done
            println("    wait end")
        }()
        defer func() {
            println("  notify start")
            done <- true
            println("  notify end")
        }()
    }
    func main() {
        stuff()
    }
    

    【讨论】:

    • Sleep 呼叫仅作为可能阻塞或不阻塞呼叫的占位符。在调用done &lt;- true 之前,&lt;-done 不应该阻塞吗?
    • 确实如此,但是 main 退出并且 os.Stdout 在打印“wait end”之前被关闭,现在如果你将它移动到一个单独的函数并从 main 调用它,所有这些都会在 main 退出之前发生所以一切都按预期工作。
    • 我不认为将其移至单独的函数有助于解决意外输出。例如,play.golang.org/p/SgyLMjCS-0 会导致相同的问题。但是,你是正确的 os.Stdout 提前关闭。
    • @jojaba 很奇怪,我一直在本地测试它并按预期工作,一定是 1.4 中的东西。
    • 不,这里没有多少功能会有所作为。基本问题是期望从异步运行的代码中产生结果,而程序中没有任何表示它应该等待的东西。这是标准的func main() { go fmt.Printf("Hello world!\n") },似乎以某种方式混淆了人们。
    【解决方案2】:

    done 通道上的事件序列:“[当] 通道无缓冲时,只有在发送方和接收方都准备好时,通信才会成功。”以您为例,

    done 频道未缓冲:main

    done := make(chan bool)
    

    接收等待发送:go func()

    <-done 
    

    接收就绪 (&lt;-done),发送:defer func()

    done <- true
    

    main 函数结束并且不等待 goroutine (go func()) 完成。

    输出:

    enter
        wait start
      notify start
      notify end
    exit
    

    The Go Programming Language Specification

    Channel types

    通道提供了一种同时执行功能的机制 通过发送和接收指定元素的值进行通信 类型。未初始化通道的值为 nil。

    一个新的、初始化的通道值可以使用内置的 函数 make,它接受通道类型和可选容量 作为参数:

    make(chan int, 100)
    

    容量,以元素数量为单位,设置缓冲区的大小 这个频道。如果容量为零或不存在,则通道为 无缓冲和通信成功,只有当发送者和 接收器已准备就绪。否则,通道被缓冲并且 如果缓冲区未满,则通信成功且不阻塞 (发送)或不为空(接收)。 nil 通道永远不会准备好 交流。

    Go statements

    程序执行从初始化主包开始,然后 调用main函数。当该函数调用返回时, 程序退出。它不会等待其他(非主)goroutines 完成。

    【讨论】:

    • 虽然这是很好的一般信息,但它并不能回答我的问题。它只是重申了有问题的代码已经做了什么。
    • 谢谢,您对程序提前退出的编辑回复解决了这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-12-05
    • 2017-09-20
    • 2018-03-15
    • 2016-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多