func main() {
c := make(chan os.Signal, 1)
signal.Notify(c)
ticker := time.NewTicker(time.Second)
stop := make(chan bool)
go func() {
defer func() { stop <- true }()
for {
select {
case <-ticker.C:
fmt.Println("Tick")
case <-stop:
fmt.Println("Goroutine closing")
return
}
}
}()
<-c
ticker.Stop()
stop <- true
<-stop
fmt.Println("Application stopped")
}
无论我运行多少次上面的代码,我都得到了相同的结果。也就是说,按下Ctrl + C后,“应用程序停止”之前始终打印“Goroutine关闭”。
我认为,理论上,“Goroutine关闭”有可能根本不打印。我对吗?不幸的是,我从未得到过这个理论结果。
顺便说一句:我知道应该避免在一个例行程序中读写一个频道。暂时忽略它。
在你的情况下,Goroutine closing
将始终执行,它将始终在Application stopped
之前打印,因为你的stop
通道没有缓冲。这意味着发送将阻塞,直到收到结果。
在你的代码中,stop <- true
中的main
将阻塞,直到goroutine收到该值,导致该通道再次为空。然后你的主要的<-stop
将阻止,直到另一个值发送到通道,这发生在你的goroutine打印Goroutine closing
后返回。
如果您要以缓冲方式初始化频道
stop := make(chan bool, 1)
那么Goroutine closing
可能不会被执行。要看到这一点,你可以在打印time.Sleep
之后立即添加一个Tick
,因为这样更容易发生这种情况(每次在睡眠期间按Ctrl + C都会发生这种情况)。
使用sync.WaitGroup
等待goroutines完成是一个很好的选择,特别是如果你不得不等待多个goroutine。你也可以使用context.Context
来阻止goroutines。重新编写代码以使用这两种方法可能如下所示:
func main() {
c := make(chan os.Signal, 1)
signal.Notify(c)
ticker := time.NewTicker(time.Second)
ctx, cancel := context.WithCancel(context.Background())
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer func() { wg.Done() }()
for {
select {
case <-ctx.Done():
fmt.Println("Goroutine closing")
return
case <-ticker.C:
fmt.Println("Tick")
time.Sleep(time.Second)
}
}
}()
<-c
ticker.Stop()
cancel()
wg.Wait()
fmt.Println("Application stopped")
}