如果包含 time.Sleep,Goroutine 不会执行
Posted
技术标签:
【中文标题】如果包含 time.Sleep,Goroutine 不会执行【英文标题】:Goroutine does not execute if time.Sleep included 【发布时间】:2015-04-03 04:08:21 【问题描述】:以下代码运行良好:
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
playgound_1
如果我改变了
c<-3
到
time.Sleep(time.Second)
c<-3
playground_2
我的代码没有执行。
我的直觉是 main
在 my_func
完成执行之前返回,但似乎添加暂停应该没有任何效果。我完全迷失在这个简单的例子中,这是怎么回事?
【问题讨论】:
【参考方案1】:当main
函数结束时,程序也随之结束。它不会等待其他 goroutine 完成。
引用Go Language Specification: Program Execution:
程序执行从初始化主包开始,然后调用函数
main
。当该函数调用返回时,程序退出。它不会等待其他(非main
)goroutine 完成。
因此,当您的main
函数通过在通道上发送值而成功时,程序可能会立即终止,在另一个 goroutine 有机会将接收到的值打印到控制台之前。
如果要确保将值打印到控制台,则必须将其与退出 main
函数的事件同步:
以“完成”频道为例(在 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
上发送的值之后已收到并打印到控制台。
对看似不确定的运行的解释:
Goroutines 可能会或可能不会并行执行同时。同步确保某些事件在其他事件之前发生。这是您获得的唯一保证,也是您唯一应该依赖的东西。 2 个例子发生在之前:
启动新 goroutine 的 go
语句发生在 goroutine 开始执行之前。
通道上的发送发生在来自该通道的相应接收完成之前。
更多详情请阅读The Go Memory Model。
回到你的例子:
来自无缓冲通道的接收发生在该通道上的发送完成之前。
所以你得到的唯一保证是运行my_func()
的goroutine将接收来自main()
的通道c
的值。但是一旦接收到值,main
函数可能 会继续,但由于发送后没有更多的语句,它只是与程序一起结束。非main
协程是否有时间 或机会 使用fmt.Println()
打印它未定义。
【讨论】:
你能解释一下为什么在我的第一个示例中my_func
在主要结束之前完成,但在第二个示例中没有?
我认为这是正确的答案,但如果添加一些信息可能会更好:一种(或多种)让示例代码等待的方法......
添加说明和示例如何确保在通道上发送的值被打印。
我想我明白了,但我想确定一下。我的理解是否正确:“第一个和第二个示例,我的 goroutine 可能会或可能无法在 main 完成之前完成,而它发生在第一个示例而不是第二个示例中的事实只是纯粹的机会。
@Akavall 是的,在这两种情况下,值可能会或可能不会被打印出来。请注意,在 Go Playground 上,程序的结果会被缓存。因此,如果在某种情况下它被打印出来,运行相同的程序总是会打印它(直到有人删除了程序的输出缓存)。以上是关于如果包含 time.Sleep,Goroutine 不会执行的主要内容,如果未能解决你的问题,请参考以下文章