在Windows上正常终止进程

I'm writing a server application in Go, and using a wrapper to run it as a Windows service.

There's a need to gracefully shut down the server (to close resources and connections properly), and in UNIX it would be handled through the SIGTERM signal. No big deal.

Though on Windows things seem very different. I see on this guide that signals actually exist on windows (?), and the SIGTERM is defined, though other pages indicate they don't, or to use other mechanisms like WM_CLOSE.

What is the preferable way to tell a headless process to gracefully terminate? How should it be implemented in Go?

The server is designed to be multiplatform, so the most standard way of doing it is preferable.

If you are running some form of web-services, may be better to use the web-service itself to initiate a shutdown, as tracking PIDS for signals can get messy.

To stop a http web-service, simply add a route like this:

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 source-code example.

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

The go way to initiate canceling a task/service, is to use the context.Context package.

So if you want a signal handler to trigger the closing of a task's 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
}

and then pass the returned taskCtx on to your worker task for it to listen on.

select {
    case <-taskCtx.Done():
        // context was canceled
    default: // poll - rather than block in this example
}

Playground source-code.

Output:

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

EDIT:

I tested this on Windows 10 and the clean-up is triggered when a Ctrl-C is issued from the same console. Not sure how to send signals externally on Windows - which may be the OP's original issue. Using say killtask /F /PID 33186 would indeed kill the process without any signal handler being triggered.