在 Go 中处理前台信号
Handle a signal in the foreground in Go
我想在后台监听 OS 信号,但在前台处理它。
这是示例代码。
主循环无限期地每秒打印一次“boop”。
但是当接收到中断信号时,处理程序会打印“缓慢终止...”并在终止程序之前等待五秒钟。
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)
}
}
当正在处理信号时,我希望其他所有内容都被阻止。
但是目前在缓慢终止的五秒钟内,它一直在打印“boop”。
我明白了:
boop
boop
^CTerminating slowly...
boop
boop
boop
boop
boop
我想要这个:
boop
boop
^CTerminating slowly...
真正的程序是一个基于堆栈的语言解释器,用户可以在终止时有一些东西运行,但目前主循环可以同时更改堆栈。
作为,这里需要一个select
语句。一旦将循环主体包装在 select
的默认情况下,就不再需要启动另一个 goroutine;使用 select
案例来完成这项工作。
此外,您使用的是无缓冲通道,但根据 Notify
's documentation,该函数需要适当缓冲的通道:
Package signal will not block sending to c: the caller must ensure that c has sufficient buffer space to keep up with the expected signal rate. For a channel used for notification of just one signal value, a buffer of size 1 is sufficient.
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)
}
}
}
一旦信号发送到通道 c
,non-default 案例就会变成 non-blocking 并得到 select
ed,函数结束。
这是程序的一种可能输出(在那个特定的执行过程中,我在大约 4 秒后点击了 ^C
):
boop
boop
boop
boop
^CTerminating slowly...
虽然 select
的答案绝对是最好的,但我只想补充一点,如果您翻转逻辑,结果可能还可以:
package main
import (
"fmt"
"os"
"os/signal"
"time"
)
func main() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
go func() {
for {
fmt.Println("boop")
time.Sleep(time.Duration(1) * time.Second)
}
}()
<-c
fmt.Println("Terminating slowly...")
time.Sleep(time.Duration(5) * time.Second)
os.Exit(0)
}
我想在后台监听 OS 信号,但在前台处理它。
这是示例代码。 主循环无限期地每秒打印一次“boop”。 但是当接收到中断信号时,处理程序会打印“缓慢终止...”并在终止程序之前等待五秒钟。
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)
}
}
当正在处理信号时,我希望其他所有内容都被阻止。 但是目前在缓慢终止的五秒钟内,它一直在打印“boop”。
我明白了:
boop
boop
^CTerminating slowly...
boop
boop
boop
boop
boop
我想要这个:
boop
boop
^CTerminating slowly...
真正的程序是一个基于堆栈的语言解释器,用户可以在终止时有一些东西运行,但目前主循环可以同时更改堆栈。
作为select
语句。一旦将循环主体包装在 select
的默认情况下,就不再需要启动另一个 goroutine;使用 select
案例来完成这项工作。
此外,您使用的是无缓冲通道,但根据 Notify
's documentation,该函数需要适当缓冲的通道:
Package signal will not block sending to c: the caller must ensure that c has sufficient buffer space to keep up with the expected signal rate. For a channel used for notification of just one signal value, a buffer of size 1 is sufficient.
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)
}
}
}
一旦信号发送到通道 c
,non-default 案例就会变成 non-blocking 并得到 select
ed,函数结束。
这是程序的一种可能输出(在那个特定的执行过程中,我在大约 4 秒后点击了 ^C
):
boop
boop
boop
boop
^CTerminating slowly...
虽然 select
的答案绝对是最好的,但我只想补充一点,如果您翻转逻辑,结果可能还可以:
package main
import (
"fmt"
"os"
"os/signal"
"time"
)
func main() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt)
go func() {
for {
fmt.Println("boop")
time.Sleep(time.Duration(1) * time.Second)
}
}()
<-c
fmt.Println("Terminating slowly...")
time.Sleep(time.Duration(5) * time.Second)
os.Exit(0)
}