是否有可能捕获一个Ctrl+C信号(SIGINT)并运行一个清理函数,在一个&;延迟&;时尚吗?

我想捕获从控制台发送的Ctrl + C (SIGINT)信号,并打印出一些部分运行总数。

138402 次浏览

你可以使用os /信号包来处理传入信号。Ctrl+C信号情报,所以你可以用它来捕获os.Interrupt

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func(){
for sig := range c {
// sig is a ^C, handle it
}
}()

终止程序并打印信息的方式完全取决于您。

(在发布的时候)上面接受的答案中有一两个小错字,所以这里是清理后的版本。在这个例子中,当接收到Ctrl+C时,我将停止CPU分析器。

// capture ctrl+c and stop CPU profiler
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for sig := range c {
log.Printf("captured %v, stopping profiler and exiting..", sig)
pprof.StopCPUProfile()
os.Exit(1)
}
}()

稍微补充一下其他答案,如果你真的想捕获SIGTERM (kill命令发送的默认信号),你可以使用syscall.SIGTERM来代替os.Interrupt。注意,syscall接口是特定于系统的,可能不是在任何地方都适用(例如在windows上)。但它很好地抓住了两者:

c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....

如此:

package main


import (
"fmt"
"os"
"os/signal"
"syscall"
"time" // or "runtime"
)


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


func main() {
c := make(chan os.Signal)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
cleanup()
os.Exit(1)
}()


for {
fmt.Println("sleeping...")
time.Sleep(10 * time.Second) // or runtime.Gosched() or similar per @misterbee
}
}

操场上中签出

上面所有的方法在拼接时都可以工作,但是Gobyexample的信号页面有一个非常干净和完整的信号捕获示例。值得在这个列表中加上。

package main


import (
"fmt"
"os"
"os/signal"
"syscall"
)


func main() {
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)


signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)


go func() {
sig := <-sigs
fmt.Println()
fmt.Println(sig)
done <- true
}()


fmt.Println("awaiting signal")
<-done
fmt.Println("exiting")
}

来源:gobyexample.com/signals

死亡是一个简单的库,它使用通道和等待组来等待关闭信号。一旦接收到信号,它就会在所有你想要清除的结构上调用close方法。

您可以使用不同的gor例程来检测系统调用。SIGINT和系统调用。SIGTERM信号,并使用信号。通知将它们中继到一个通道。您可以使用通道发送一个钩子到该goroutine,并将其保存在函数片中。当在通道上检测到关闭信号时,您可以在片中执行这些功能。这可用于清理资源、等待运行goroutine完成、持久化数据或打印部分运行总数。

我写了一个小而简单的实用程序来在关机时添加和运行钩子。希望对大家有所帮助。

https://github.com/ankit-arora/go-utils/blob/master/go-shutdown-hook/shutdown-hook.go

你可以用“延迟”的方式来做到这一点。

优雅地关闭服务器:

srv := &http.Server{}


go_shutdown_hook.ADD(func() {
log.Println("shutting down server")
srv.Shutdown(nil)
log.Println("shutting down server-done")
})


l, err := net.Listen("tcp", ":3090")


log.Println(srv.Serve(l))


go_shutdown_hook.Wait()

只是为了记录如果有人需要一种方法来处理Windows上的信号。

我有一个要求处理从程序a调用程序B通过os/exec,但程序B永远无法优雅地终止,因为通过cmd.Process.Signal(syscall.SIGTERM)或其他信号发送信号在Windows上不支持。

我通过创建一个临时文件作为信号来处理这个问题。例如,程序A创建文件.signal.term,程序B需要以间隔为基数检查该文件是否存在。如果文件存在,它将退出程序,并在需要时处理清理。

我相信还有其他方法,但这招管用。

这是另一个版本,如果你有一些任务要清理。代码将在它们的方法中留下清理过程。

package main


import (
"fmt"
"os"
"os/signal"
"syscall"


)






func main() {


_,done1:=doSomething1()
_,done2:=doSomething2()


//do main thread




println("wait for finish")
<-done1
<-done2
fmt.Print("clean up done, can exit safely")


}


func doSomething1() (error, chan bool) {
//do something
done:=make(chan bool)
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
//cleanup of something1
done<-true
}()
return nil,done
}




func doSomething2() (error, chan bool) {
//do something
done:=make(chan bool)
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
go func() {
<-c
//cleanup of something2
done<-true
}()
return nil,done
}

如果你需要清理主函数,你也需要在主线程中使用go func()捕获信号。

看例子

当我们运行这个程序时,它会阻塞等待一个信号。通过输入ctrl-C(终端显示为^C),我们可以发送一个SIGINT信号,使程序打印中断,然后退出。

信号。Notify注册给定通道以接收指定信号的通知。

package main


import (
"fmt"
"os"
"os/signal"
"syscall"
)


func main() {


sig := make(chan os.Signal, 1)
done := make(chan bool, 1)


signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)


go func() {
sig := <-sig
fmt.Println()
fmt.Println(sig)
done <- true


fmt.Println("ctrl+c")
}()


fmt.Println("awaiting signal")
<-done
fmt.Println("exiting")
}

检测HTTP请求取消




package main


import (
"fmt"
"net/http"
"time"
)


func main() {


mux := http.NewServeMux()
mux.HandleFunc("/path", func(writer http.ResponseWriter, request *http.Request) {


time.Sleep(time.Second * 5)


select {
case <-time.After(time.Millisecond * 10):


fmt.Println("started")
return
case <-request.Context().Done():
fmt.Println("canceled")
}
})


http.ListenAndServe(":8000", mux)


}