[英]Gracefully terminate a process on Windows
該go
的方式來啟動取消任務/服務,是使用context.Context包。
因此,如果您希望信號處理程序觸發關閉任務的context.Context
:
func registerSigHandler() context.Context {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
rootCtx := context.Background()
taskCtx, cancelFn := context.WithCancel(rootCtx)
go func() {
sig := <-sigCh
log.Println("received signal:", sig)
// let sub-task know to wrap up: cancel taskCtx
cancelFn()
}()
return taskCtx
}
然后將返回的taskCtx
傳遞給您的工作任務以供其監聽。
select {
case <-taskCtx.Done():
// context was canceled
default: // poll - rather than block in this example
}
游樂場源代碼。
輸出:
2019/03/10 19:18:51 Worker PID: 33186
2019/03/10 19:18:51 Will terminate on these signals: interrupt terminated
2019/03/10 19:18:51 2019-03-10 19:18:51.018962 -0400 EDT m=+0.001727305
2019/03/10 19:18:52 2019-03-10 19:18:52.022782 -0400 EDT m=+1.005517010
2019/03/10 19:18:53 2019-03-10 19:18:53.019925 -0400 EDT m=+2.002630457
$ kill -INT 33186
2019/03/10 19:18:53 received signal: interrupt
2019/03/10 19:18:53 task context terminated reason: context canceled
2019/03/10 19:18:53 wrapping up task...
2019/03/10 19:18:53 workerTask() exited cleanly
2019/03/10 19:18:53 main exited cleanly
編輯:
我在Windows 10
上對此進行了測試,當從同一控制台發出 Ctrl-C 時觸發清理。 不確定如何在 Windows 上從外部發送信號 - 這可能是 OP 的原始問題。 使用 say killtask /F /PID 33186
確實會在沒有觸發任何信號處理程序的情況下killtask /F /PID 33186
進程。
所以,過了一會兒,我只想分享我是如何解決它的。 這很丑陋,但我找到的唯一真正的方法。 Windows golang 程序確實會偵聽 CTRL+C,這不是信號或類似信號,但它確實使用 UNIX 具有的相同信號在 go 程序中觸發了正常關閉。
這是相關的代碼:
// Create shutdown channel
exitChan := make(chan int)
interruptChan := make(chan os.Signal, 1)
signal.Notify(interruptChan, os.Interrupt)
select {
case exitCode := <-exitChan:
os.Exit(exitCode)
case <-interruptChan:
// This works in Windows if ctrl-c is invoked. It sucks though
svr.Stop()
os.Exit(-1)
}
為了在給定的過程中觸發 CTRL+C,我使用了一個名為 windows-kill.exe 的小程序
// Windows is "special" and we need to use a library for killing processes gracefully
// Unfortunately setting up the C++ library with go is a hassle, so we resort to a CLI tool
ex, _ := os.Executable()
cmdPath := filepath.Join(filepath.Dir(ex), "windows-kill.exe")
cmd := exec.CommandContext(context.Background(), cmdPath, "-SIGINT", strconv.Itoa(l.proc.Pid))
err := cmd.Start()
if err != nil {
panic(err)
}
這是庫和工具的 repo: https : //github.com/alirdn/windows-kill
將 *nix 構造和函數用於 Windows 的想法通常很糟糕,並且容易出現可能有效也可能無效的奇怪技巧。
在關閉 GUI 應用程序時進行清理的正確方法是處理WM_QUERYENDSESSION和WM_ENDSESSION 。 此外,您可以靈活使用通用關閉機制,請在此處查看。
如果您是服務,則獲得通知的正確方法是控制台應用程序的服務處理程序 (SERVICE_STOPPED)。 有關更多信息,請參閱編寫 ServiceMain 。
如果您正在運行某種形式的網絡服務,最好使用網絡服務本身來啟動關閉,因為跟蹤信號的 PIDS 可能會變得混亂。
要停止 http 網絡服務,只需添加如下路由:
http.HandleFunc("/shutdown",
func(w http.ResponseWriter, r *http.Request) {
// stops server - may want to add admin credentials check here!
srv.Shutdown(context.Background())
})
Playground源代碼示例。
2019/03/10 16:58:15 Listening on: :8080
$ curl 'localhost:8080/shutdown'
2019/03/10 17:04:17 Listening on: :8080
2019/03/10 17:04:19 exited cleanly
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.