英文:
Why Go channel is blocking but not quitting
问题
我有两个版本来实现使用signal.NotifyContext在信号上进行上下文取消。
版本1 https://play.golang.org/p/rwOnYEgPecE
func main() {
ch := run()
<-ch
}
func run() chan bool {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
var done = make(chan bool)
//go func() {
select {
case <-ctx.Done():
fmt.Println("Quitting")
stop()
done <- true
}
//}()
return done
}
版本2 https://play.golang.org/p/oijbICeSrNT
func main() {
ch := run()
<-ch
}
func run() chan bool {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
var done = make(chan bool)
go func() {
select {
case <-ctx.Done():
fmt.Println("Quitting")
stop()
done <- true
}
}()
return done
}
为什么第一个版本打印出Quitting
这一行,但没有退出,而第二个版本打印并正确退出?
英文:
I have those two versions to implement context cancellation over signals using signal.NotifyContext
Version 1 https://play.golang.org/p/rwOnYEgPecE
func main() {
ch := run()
<-ch
}
func run() chan bool {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
var done = make(chan bool)
//go func() {
select {
case <-ctx.Done():
fmt.Println("Quitting")
stop()
done <- true
}
//}()
return done
}
Version 2 https://play.golang.org/p/oijbICeSrNT
func main() {
ch := run()
<-ch
}
func run() chan bool {
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT)
var done = make(chan bool)
go func() {
select {
case <-ctx.Done():
fmt.Println("Quitting")
stop()
done <- true
}
}()
return done
}
Why is the first version printing the line Quitting
but does not exit, while the second version prints and quits properly?
答案1
得分: 3
第一个案例不按照你的期望行为的原因是因为所有的代码都在一个单独的(主)goroutine中运行。
select {
case <-ctx.Done():
fmt.Println("Quitting")
stop()
done <- true // 这里会阻塞,因为没有人在监听
}
}
在第二个例子中,由于上述逻辑在一个goroutine中运行,所以main()
函数将会监听该通道。
第二种方法更好,因为任何信号处理程序(在程序的整个生命周期内运行)都应该在自己的goroutine中运行。
英文:
The reason the first case doesn't behave as you expect is because everything is running in a single (main) goroutine.
select {
case <-ctx.Done():
fmt.Println("Quitting")
stop()
done <- true // this blocks because no one is listening
}
}
in your second example because the above logic is run in a goroutine, then main()
will be listening on the channel.
The second method is preferred, since any signal handler - which is running for the lifetime of a program - should be running in its own goroutine.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论