英文:
Golang inbound channel not receiving inside a 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)
}
英文:
Please help me understand why the inbound <-done
channel is not being picked up in this case?
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.
}
I'm expecting the output to be:
enter
notify start
wait start
wait end
notify end
exit
But instead it's:
enter
wait start
notify start
notify end
exit
I initially assumed that the done
channel was somehow being closed or cleaned up early, but it results in the same unexpected behavior even when the done
is global.
Shouldn't <-done
block until done <- true
occurs? And vice versa?
Resolution
It seems that I was expecting the program to wait for all of the goroutines to completed before exiting. This is an incorrect assumption.
Here's an dirty workaround:
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)
}
答案1
得分: 2
当你使用sleep
函数时,它会给goroutine一些时间来启动,然后在它从通道中读取之前,主函数就已经退出了,所以最后的println(" wait end")
没有被调用。
然而,如果你不调用sleep
函数,defer
语句将会阻塞,直到goroutine从通道中读取数据,这给它足够的时间来打印输出。
如果你将这段代码移到一个不同的函数中,并从主函数中调用它,它将按预期工作。
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()
}
英文:
When you use sleep, it gives time for the goroutine to start, then by the time it reads from the channel, main exits before the last println(" wait end")
gets called.
However if you don't call sleep, defer will block until the goroutine reads from it and that gives it enough time to print.
If you move the code to a different function and call it from main it will work as expected.
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()
}
答案2
得分: 1
done
通道上的事件顺序是:“当通道是无缓冲的时候,只有在发送方和接收方都准备好时,通信才会成功。”对于你的示例,
done
通道是无缓冲的:main
。
done := make(chan bool)
接收等待发送:go func()
。
<-done
接收准备好(<-done
),发送:defer func()
。
done <- true
main
函数结束后不等待goroutine(go func()
)完成。
输出:
进入
等待开始
通知开始
通知结束
退出
通道提供了一种机制,用于并发执行函数之间通过发送和接收指定元素类型的值进行通信。未初始化的通道的值为nil。
可以使用内置函数make创建一个新的初始化的通道值,该函数接受通道类型和可选的容量作为参数:
make(chan int, 100)
容量(以元素数量表示)设置通道中缓冲区的大小。如果容量为零或不存在,则通道是无缓冲的,只有在发送方和接收方都准备好时通信才会成功。否则,通道是有缓冲的,如果缓冲区不满(发送)或不空(接收),通信将成功而不会阻塞。nil通道永远不会准备好进行通信。
程序的执行从初始化主包开始,然后调用main函数。当该函数调用返回时,程序退出。它不会等待其他(非主)goroutine 完成。
英文:
The sequence of events on the done
channel: "[when] the channel is unbuffered communication succeeds only when both a sender and receiver are ready." For your example,
The done
channel is unbuffered: main
.
done := make(chan bool)
Receive waiting for a send: go func()
.
<-done
Receive ready (<-done
), send: defer func()
.
done <- true
The main
function ends and does not wait for the goroutine (go func()
) to finish.
Output:
enter
wait start
notify start
notify end
exit
> The Go Programming Language
> Specification
>
> Channel types
>
> A channel provides a mechanism for concurrently executing functions to
> communicate by sending and receiving values of a specified element
> type. The value of an uninitialized channel is nil.
>
> A new, initialized channel value can be made using the built-in
> function make, which takes the channel type and an optional capacity
> as arguments:
>
> make(chan int, 100)
>
> The capacity, in number of elements, sets the size of the buffer in
> the channel. If the capacity is zero or absent, the channel is
> unbuffered and communication succeeds only when both a sender and
> receiver are ready. Otherwise, the channel is buffered and
> communication succeeds without blocking if the buffer is not full
> (sends) or not empty (receives). A nil channel is never ready for
> communication.
>
> Go statements
>
> Program execution begins by initializing the main package and then
> invoking the function main. When that function invocation returns, the
> program exits. It does not wait for other (non-main) goroutines to
> complete.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论