我已将示例https://gobyexample.com/tickers修改为以下脚本:
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
go func() {
for {
select {
case <-done:
fmt.Println("Received 'done'")
return
case t := <-ticker.C:
fmt.Println("Tick at", t)
}
}
}()
time.Sleep(1600 * time.Millisecond)
// ticker.Stop()
done <- true
// fmt.Println("Ticker stopped.")
}
与所引用示例的两个区别是,我已注释掉ticker.Stop()
行,并在fmt.Println("Received 'done'")
块中添加了case <-done
行。如果运行此命令,则会观察到以下输出:
> go run tickers.go
Tick at 2019-10-06 15:25:50.576798 -0700 PDT m=+0.504913907
Tick at 2019-10-06 15:25:51.074993 -0700 PDT m=+1.003102855
Tick at 2019-10-06 15:25:51.576418 -0700 PDT m=+1.504521538
我的问题:为什么不将Received 'done'
打印到终端?
奇怪的是,如果我在Ticker stopped
Println语句中发表评论,我也会看到Received 'done'
:
> go run tickers.go
Tick at 2019-10-06 15:27:30.735163 -0700 PDT m=+0.504666656
Tick at 2019-10-06 15:27:31.234076 -0700 PDT m=+1.003573649
Tick at 2019-10-06 15:27:31.735342 -0700 PDT m=+1.504833296
Ticker stopped.
Received 'done'
我记得,可以假定Goroutine中的代码是同步运行的,所以我感到困惑的是,在前一种情况下我看不到Println
语句的影响,因为它发生在Goroutine返回。有人可以解释吗?
答案 0 :(得分:2)
发生done <- true
时,主函数直接返回。
您可以添加另一个时间。Sleep()看看发生了什么。
time.Sleep(1600 * time.Millisecond)
// ticker.Stop()
done <- true
// fmt.Println("Ticker stopped.")
time.Sleep(1600 * time.Millisecond)
答案 1 :(得分:1)
...为什么不将“接收到的”“完成”打印到终端?
确实如此,或者更确切地说,它尝试 。
当主goroutine(称为程序包main
的{main
)返回时(或者在各种情况下不早于此),Go程序退出。您的main
会在调用time.Sleep()
之后在true
上发送done
之后返回。
同时,for
值到达true
通道时,done
循环中的goroutine会唤醒。这是在主goroutine发送完之后发生的,之后主goroutine正在退出。
如果在退出过程中主goroutine花费了足够长的时间,则匿名goroutine将有时间打印Received 'done'
。如果在退出过程中主goroutine足够快,则匿名goroutine永远不会完成,甚至可能永远不会开始,打印任何内容,而您什么也看不到。 (实际输出是通过单个基础系统调用完成的,因此您可以全部获得,也可以全部不获得。)
您可以通过各种机制确保在退出主程序之前就完成goroutine,但是最简单的方法可能是使用sync.WaitGroup
,因为它是为此目的而设计的。创建一个等待组,将其计数器设置为1(向其初始零添加1),然后在退出匿名goroutine的途中调用Done
函数。让主goroutine等待它。