一尘不染

在这种情况下,Go HTTP处理程序goroutine是否应立即退出?

go

我有一个这样的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将立即终止!

那么,这是理想的行为,还是我做错了什么?

如果可以预料,由于任何goroutine将完成其工作,那么提前取消的意义何在?

如果我做错了什么,请帮我指出正确的方法。


阅读 242

收藏
2020-07-02

共1个答案

一尘不染

您创建了一个contex.Context可以取消的,您可以在客户端关闭连接时取消它,但是您不会检查上下文,并且如果取消它,处理程序也不会做任何不同的事情。上下文仅
携带 超时和取消信号,它不具有杀死/终止goroutines的能力或意图。goroutine本身必须监视此类取消信号并对其采取行动。

因此,您看到的是代码的预期输出。

您想要监视的是上下文,如果上下文被取消,则从处理程序中“立即”返回。

当然,如果您正在“睡觉”,则无法同时监视上下文。因此,请改用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......")
})
2020-07-02