我有一个这样的Go HTTP处理程序:
mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if cn, ok := w.(http.CloseNotifier); ok { go func(done <-chan struct{}, closed <-chan bool) { select { case <-done: case <-closed: fmt.Println("client cancelled....................!!!!!!!!!") cancel() } }(ctx.Done(), cn.CloseNotify()) } time.Sleep(5 * time.Second) fmt.Println("I am still running...........") fmt.Fprint(w, "cancellation testing......") })
该API正常工作,然后使用curl在请求完成之前我故意用终止curl命令Control-C,在服务器端我确实看到了client cancelled....................!!!!!!!!!get注销,但过一会儿I am still running...........get退出后,我还以为这个goroutine将立即终止!
Control-C
client cancelled....................!!!!!!!!!
I am still running...........
那么,这是理想的行为,还是我做错了什么?
如果可以预料,由于任何goroutine将完成其工作,那么提前取消的意义何在?
如果我做错了什么,请帮我指出正确的方法。
您创建了一个contex.Context可以取消的,您可以在客户端关闭连接时取消它,但是您不会检查上下文,并且如果取消它,处理程序也不会做任何不同的事情。上下文仅 携带 超时和取消信号,它不具有杀死/终止goroutines的能力或意图。goroutine本身必须监视此类取消信号并对其采取行动。
contex.Context
因此,您看到的是代码的预期输出。
您想要监视的是上下文,如果上下文被取消,则从处理程序中“立即”返回。
当然,如果您正在“睡觉”,则无法同时监视上下文。因此,请改用time.After(),如本例所示:
time.After()
mux.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() if cn, ok := w.(http.CloseNotifier); ok { go func(done <-chan struct{}, closed <-chan bool) { select { case <-done: case <-closed: fmt.Println("client cancelled....................!!!!!!!!!") cancel() } }(ctx.Done(), cn.CloseNotify()) } select { case <-time.After(5 * time.Second): fmt.Println("5 seconds elapsed, client didn't close") case <-ctx.Done(): fmt.Println("Context closed, client closed connection?") return } fmt.Fprint(w, "cancellation testing......") })