请注意,如果使用来“安装”信号通道
signal.Notify(),则默认行为将被禁用。这意味着如果您这样做,函数中的
for循环
fn()将不会中断,它将继续运行。
因此,当您在注册的频道上收到一个值时,必须使该
for循环终止,以便进行“清除”清除。否则,
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:在启动应在退出
WaitGroup.Done()时等待的goroutine时向其添加1,并在此类goroutine完成时调用。
同样,由于在实际应用中可能有很多这样的信号处理,因此信号处理应移到“中央”位置,而不是在每个位置都进行。
这是一个完整的示例,该如何做:
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...")}这是上面的应用
CTRL+C在启动后三秒钟按的示例输出:
working...working...working...^Ccleaning up...



