GGG*_*GGG 1 signals foreground go
我想在后台监听操作系统信号,但在前台处理它。
这是示例代码。主循环无限期地每秒打印“boop”。但是,当收到中断信号时,处理程序会打印“Terminate Slow...”,并等待五秒钟,然后再终止程序。
package main
import (
"fmt"
"os"
"os/signal"
"time"
)
func main() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
go func() {
<-c
fmt.Println("Terminating slowly...")
time.Sleep(time.Duration(5)*time.Second)
os.Exit(0)
}()
for {
fmt.Println("boop")
time.Sleep(time.Duration(1)*time.Second)
}
}
Run Code Online (Sandbox Code Playgroud)
当信号被处理时,我希望其他一切都被阻止。但目前在缓慢终止的五秒内,它不断打印“boop”。
我明白了:
boop
boop
^CTerminating slowly...
boop
boop
boop
boop
boop
Run Code Online (Sandbox Code Playgroud)
我想要这个:
boop
boop
^CTerminating slowly...
Run Code Online (Sandbox Code Playgroud)
真正的程序是一个基于堆栈的语言解释器,用户可以在终止时运行某些内容,但目前主循环可以同时更改堆栈。
正如mh-cbon 指出的那样,select
您在这里需要一个声明。一旦将循环体包装在 a 的默认情况下select
,您就不再需要启动另一个 goroutine;使用select
案例来完成这项工作。
另外,您使用的是无缓冲通道,但根据Notify
的文档,该函数需要适当的缓冲通道:
包信号不会阻塞发送到c:调用者必须确保c有足够的缓冲空间来跟上预期的信号速率。对于仅用于通知一个信号值的通道,大小为 1 的缓冲区就足够了。
package main
import (
"fmt"
"os"
"os/signal"
"time"
)
func main() {
c := make(chan os.Signal, 1) // buffered
signal.Notify(c, os.Interrupt)
for {
select {
case <-c:
fmt.Println("Terminating slowly...")
time.Sleep(time.Duration(5) * time.Second)
return
default:
fmt.Println("boop")
time.Sleep(time.Duration(1) * time.Second)
}
}
}
Run Code Online (Sandbox Code Playgroud)
一旦信号发送到通道c
,非默认情况就变成非阻塞并被select
ed,并且函数结束。
以下是该程序的一种可能输出(在该特定执行过程中,我^C
在大约 4 秒后点击):
boop
boop
boop
boop
^CTerminating slowly...
Run Code Online (Sandbox Code Playgroud)