在 Go 中收到 SIGINT 时是否调用延迟函数?

Are deferred functions called when SIGINT is received in Go?

对于下面的代码片段,在收到 ^C 时不会进行延迟调用。清理是否有可能引入竞争条件?如果是,什么是接收中断时更好的清理模式?

  func fn() {
    // some code
    defer cleanup()
    go func() {
       c := make(chan os.Signal, 1)
       signal.Notify(c, os.Interrupt)

       // Block until a signal is received.
       _ = <-c
       cleanup()
     }
     for {
        // Infinite loop. Returns iff an error is encountered in the 
        // body
     }
}

请注意,如果您 "install" 您的信号通道 signal.Notify(),默认行为将被禁用。这意味着如果你这样做,你的 fn() 函数中的 for 循环将不会被中断,它会继续 运行.

因此,当您在注册频道上收到一个值时,您必须终止 for 循环,以便进行 "clean" 清理。否则应该释放的资源 cleanup() 可能仍在 for 中使用,很可能导致错误或恐慌。

执行此操作后,您甚至不必手动调用 cleanup(),因为从 fn() 返回将 运行 正确地延迟函数。

这是一个例子:

var shutdownCh = make(chan struct{})

func fn() {
    defer cleanup()

    go func() {
        c := make(chan os.Signal, 1)
        signal.Notify(c, os.Interrupt)
        <-c
        close(shutdownCh)
    }()

    for {
        select {
        case <-shutdownCh:
            return
            // Other cases might be listed here..
        default:
        }
        time.Sleep(time.Millisecond)
    }
}

当然上面的例子并不能保证应用程序终止。你应该有一些代码来监听 shutdownCh 并终止应用程序。此代码还应等待所有 goroutine 正常完成。为此,你可以使用 sync.WaitGroup:当你启动一个应该在退出时等待的 goroutine 时向它加 1,并在这样的 goroutine 完成时调用 WaitGroup.Done()

此外,由于在真实的应用程序中可能会有很多这样的信号处理,因此信号处理应移至 "central" 位置,而不是在每个位置都完成。

这是一个完整的示例:

var shutdownCh = make(chan struct{})
var wg = &sync.WaitGroup{}

func main() {
    wg.Add(1)
    go func() {
        defer wg.Done()
        fn()
    }()

    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    <-c
    close(shutdownCh)
    wg.Wait()
}

func fn() {
    defer cleanup()
    for {
        select {
        case <-shutdownCh:
            return
            // Other cases might be listed here..
        default:
        }
        fmt.Println("working...")
        time.Sleep(time.Second)
    }
}

func cleanup() {
    fmt.Println("cleaning up...")
}

这是上述应用程序的示例输出,在启动 3 秒后按 CTRL+C

working...
working...
working...
^Ccleaning up...