Go ticker 示例不是 select 'done' 的情况吗?
Go ticker example doesn't select the 'done' case?
我已将此示例 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()
行并在 case <-done
块中添加了 fmt.Println("Received '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 returns 之前。有人可以解释一下吗?
发生done <- true
时,main函数直接返回
您可以添加另一个 time.Sleep() 以查看发生了什么。
time.Sleep(1600 * time.Millisecond)
// ticker.Stop()
done <- true
// fmt.Println("Ticker stopped.")
time.Sleep(1600 * time.Millisecond)
... why does it not print Received 'done' to the terminal?
确实如此——或者更确切地说,它尝试。
Go 程序在主 goroutine(调用包 main
的 main
)returns(或在此处未发生的各种情况下更快)时退出。您的 main
returns 在调用 time.Sleep()
然后在 done
上发送 true
之后。
同时,for
循环中的 goroutine 在 true
值到达 done
通道时唤醒。这发生在 main goroutine 发送它之后,之后 main goroutine 正在退出。
如果在这个退出过程中,主goroutine耗时足够长,匿名goroutine就有时间打印Received 'done'
。如果在这个退出过程中,主 goroutine 足够快,匿名 goroutine 永远不会完成,或者甚至永远不会开始,打印任何东西,你什么也看不到。 (实际输出由单个底层系统调用完成,因此您要么获取所有输出,要么获取其中的 none。)
您可以通过多种机制确保您分离的 goroutine 在让您的主要 goroutine 退出之前完成,但最简单的可能是使用 sync.WaitGroup
,因为它是为此而设计的。创建一个等待组,将其计数器设置为 1(将其初始零加 1),然后在退出匿名 goroutine 的途中调用 Done
函数。让主 goroutine 等待它。
我已将此示例 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()
行并在 case <-done
块中添加了 fmt.Println("Received '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 returns 之前。有人可以解释一下吗?
发生done <- true
时,main函数直接返回
您可以添加另一个 time.Sleep() 以查看发生了什么。
time.Sleep(1600 * time.Millisecond)
// ticker.Stop()
done <- true
// fmt.Println("Ticker stopped.")
time.Sleep(1600 * time.Millisecond)
... why does it not print Received 'done' to the terminal?
确实如此——或者更确切地说,它尝试。
Go 程序在主 goroutine(调用包 main
的 main
)returns(或在此处未发生的各种情况下更快)时退出。您的 main
returns 在调用 time.Sleep()
然后在 done
上发送 true
之后。
同时,for
循环中的 goroutine 在 true
值到达 done
通道时唤醒。这发生在 main goroutine 发送它之后,之后 main goroutine 正在退出。
如果在这个退出过程中,主goroutine耗时足够长,匿名goroutine就有时间打印Received 'done'
。如果在这个退出过程中,主 goroutine 足够快,匿名 goroutine 永远不会完成,或者甚至永远不会开始,打印任何东西,你什么也看不到。 (实际输出由单个底层系统调用完成,因此您要么获取所有输出,要么获取其中的 none。)
您可以通过多种机制确保您分离的 goroutine 在让您的主要 goroutine 退出之前完成,但最简单的可能是使用 sync.WaitGroup
,因为它是为此而设计的。创建一个等待组,将其计数器设置为 1(将其初始零加 1),然后在退出匿名 goroutine 的途中调用 Done
函数。让主 goroutine 等待它。