在 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 并得到 selected,函数结束。

这是程序的一种可能输出(在那个特定的执行过程中,我在大约 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)
}