我想捕获从控制台发送的 Ctrl + C (SIGINT
)信号并打印出一些部分运行总计。
这是否可以在Golang中使用?
注意:当我第一次发布问题时,我对 Ctrl + C 为SIGTERM
而不是SIGINT
感到困惑。
答案 0 :(得分:226)
您可以使用os/signal包来处理传入的信号。 ^ C是SIGINT,因此您可以使用它来捕获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
}
}()
您导致程序终止和打印信息的方式完全取决于您。
答案 1 :(得分:81)
这有效:
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
}
}
答案 2 :(得分:24)
要稍微添加其他答案,如果您确实想要捕获SIGTERM(kill命令发送的默认信号),则可以使用syscall.SIGTERM
代替os.Interrupt。请注意,系统调用接口是特定于系统的,并且可能无法在任何地方工作(例如在Windows上)。但它能很好地捕捉到两者:
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....
答案 3 :(得分:17)
在上面接受的答案中有(或在发布时)一两个小错字,所以这里是清理版本。在这个例子中,我在接收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)
}
}()
答案 4 :(得分:7)
以上所有内容似乎都在拼接时起作用,但gobyexample's signals page有一个非常干净完整的信号捕获示例。值得添加到此列表中。
答案 5 :(得分:0)
这是另一个版本,适用于您有一些要清理的任务。代码将在他们的方法中留下清理过程。
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()来捕获主线程中的信号。
答案 6 :(得分:0)
当我们运行该程序时,它将阻止等待信号。通过输入ctrl-C(终端显示为^ C),我们可以发送SIGINT信号,使程序打印中断然后退出。
信号。通知注册给定通道以接收指定信号的通知。
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)
}
答案 7 :(得分:-1)
Death是一个简单的库,它使用通道和等待组来等待关机信号。一旦收到信号,它将在你要清理的所有结构上调用close方法。
答案 8 :(得分:-1)
您可以使用不同的goroutine来检测syscall.SIGINT和syscall.SIGTERM信号,并使用signal.Notify将它们转发到通道。您可以使用通道向该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()
答案 9 :(得分:-2)
只是为了记录,如果有人需要一种方法来处理Windows上的信号。我有一个要求从prog A处理通过os / exec调用prog B但是prog B从来没有能够优雅地终止,因为通过ex发送信号。 Windows不支持cmd.Process.Signal(syscall.SIGTERM)或其他信号。我处理的方法是创建一个临时文件作为信号ex。 .signal.term通过prog A和prog B需要检查该文件是否存在于interval base上,如果文件存在,它将退出程序并在需要时处理清理,我确定还有其他方法但是这样做了工作