英文:
Goroutine does not execute if time.Sleep included
问题
以下是翻译好的内容:
以下代码运行得非常好:
package main
import (
"fmt"
)
func my_func(c chan int){
fmt.Println(<-c)
}
func main(){
c := make(chan int)
go my_func(c)
c<-3
}
然而,如果我将
c<-3
改为
time.Sleep(time.Second)
c<-3
我的代码就无法执行。
我的直觉告诉我,一些方式上 main
在 my_func
执行完之前就返回了,但是似乎添加一个暂停不应该有任何影响。对于这个简单的例子,我完全迷失了,这里发生了什么?
英文:
The following code runs perfectly fine:
package main
import (
"fmt"
)
func my_func(c chan int){
fmt.Println(<-c)
}
func main(){
c := make(chan int)
go my_func(c)
c<-3
}
However if I change
c<-3
to
time.Sleep(time.Second)
c<-3
My code does not execute.
My gut feeling is that somehow main
returns before the my_func
finishes executing, but it seems like adding a pause should not have any effect. I am totally lost on this simple example, what's going on here?
答案1
得分: 15
当main
函数结束时,程序也随之结束。它不会等待其他goroutine完成。
引用自Go语言规范:程序执行:
程序的执行从初始化主包开始,然后调用函数
main
。当该函数调用返回时,程序退出。它不会等待其他(非main
)goroutine完成。
因此,当你的main
函数成功通过通道发送值时,程序可能会立即终止,而不会等待其他goroutine将接收到的值打印到控制台。
如果你想确保值被打印到控制台,你需要将其与从main
函数退出的事件同步:
使用一个"done"通道的示例(在Go Playground上尝试):
func my_func(c, done chan int) {
fmt.Println(<-c)
done <- 1
}
func main() {
c := make(chan int)
done := make(chan int)
go my_func(c, done)
time.Sleep(time.Second)
c <- 3
<-done
}
由于done
也是一个无缓冲通道,在main
函数末尾接收它必须等待在done
通道上发送值的操作完成,而这个操作发生在接收到并打印了通道c
上的值之后。
对于看似非确定性运行的解释:
Goroutine可能会并行执行,也可能不会在同一时间执行。同步确保某些事件在其他事件之前发生。这是你所能得到的唯一保证,也是你应该依赖的唯一事情。
以下是两个示例的_Happens Before_:
- 启动新goroutine的
go
语句发生在该goroutine的执行开始之前。- 对通道的发送发生在对该通道的接收完成之前。
更多详细信息请阅读Go内存模型。
回到你的示例:
对无缓冲通道的接收发生在该通道上的发送完成之前。
因此,你所能得到的唯一保证是运行my_func()
的goroutine将接收来自main()
发送的通道c
的值。但一旦接收到值,main
函数可能会继续执行,但由于发送后没有更多的语句,它会简单地结束 - 以及整个程序。非main
的goroutine是否有时间或机会使用fmt.Println()
打印它是_未定义_的。
英文:
When the main
function ends, the program ends with it. It does not wait for other goroutines to finish.
Quoting from the Go Language Specification: Program Execution:
> 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.
So simply when your main
function succeeds by sending the value on the channel, the program might terminate immediately, before the other goroutine has the chance to print the received value to the console.
If you want to make sure the value gets printed to the console, you have to synchronize it with the event of exiting from the main
function:
Example with a "done" channel (try it on Go Playground):
func my_func(c, done chan int) {
fmt.Println(<-c)
done <- 1
}
func main() {
c := make(chan int)
done := make(chan int)
go my_func(c, done)
time.Sleep(time.Second)
c <- 3
<-done
}
Since done
is also an unbuffered channel, receiving from it at the end of the main
function must wait the sending of a value on the done
channel, which happens after the value sent on channel c
has been received and printed to the console.
Explanation for the seemingly non-deterministic runs:
Goroutines may or may not be executed parallel at the same time. Synchronization ensures that certain events happen before other events. That is the only guarantee you get, and the only thing you should rely on.
2 examples of this Happens Before:
> - The go
statement that starts a new goroutine happens before the goroutine's execution begins.
> - A send on a channel happens before the corresponding receive from that channel completes.
For more details read The Go Memory Model.
Back to your example:
> A receive from an unbuffered channel happens before the send on that channel completes.
So the only guarantee you get is that the goroutine that runs my_func()
will receive the value from channel c
sent from main()
. But once the value is received, the main
function may continue but since there is no more statements after the send, it simply ends - along with the program. Whether the non-main
goroutine will have time or chance to print it with fmt.Println()
is not defined.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论