【问题标题】:Is the Go HTTP handler goroutine expected to exit immediately in this case?在这种情况下,Go HTTP 处理程序 goroutine 是否应该立即退出?
【发布时间】:2018-05-23 09:39:18
【问题描述】:

我有一个这样的 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 我故意用 Control-C 终止 curl 命令,在服务器端我确实看到 client cancelled....................!!!!!!!!! 被注销,但过了一会儿 I am still running........... 得到也注销了,我以为这个goroutine会立即终止!

那么,这是期望的行为,还是我做错了什么?

如果是预期的,既然goroutine会完成它的工作,那么提前取消有什么意义呢?

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

【问题讨论】:

    标签: http go cancellation


    【解决方案1】:

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

    所以你看到的是你的代码的预期输出。

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

    当然,如果您正在“睡觉”,则无法同时监视上下文。所以改为使用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......")
    })
    

    【讨论】:

    • 哦,我明白了,所以如果我在这里启动新的 goroutines,也许这些 goroutines 也会启动新的,所以对于所有下游 goroutines 我需要手动监视上下文?我认为 context 包会以某种方式帮助我立即终止这些 goroutine!
    • @lnshi 上下文仅携带超时和取消信息,它无权杀死/终止goroutine。 goroutines 本身必须监控这种取消信号并对其采取行动。
    • 对不起,在监控取消后我发现我仍然不知道应该终止处理程序,我在这里添加了我的想法:play.golang.org/p/7xjjiW7XdoQ 你能帮我指出正确的方法?基本上我想实现的是,当客户端关闭连接时,我的服务器会立即终止处理程序,释放资源,不做任何剩余的工作,然后无条件退出。
    • @lnshi 如果检测到取消,您必须从处理程序返回。这表示服务该请求的 Web 服务器已结束。根据您的处理程序所做的事情,它必须监视(至少偶尔)取消通道,并且它必须终止并返回。这是你的任务,这不会自动发生。
    • 那么使用将实际工作放在 for select case 分支上的方法,并使用一次性信号发送它以通知它开始?只是觉得写了
    猜你喜欢
    • 2012-12-16
    • 1970-01-01
    • 2015-06-24
    • 2021-10-19
    • 1970-01-01
    • 1970-01-01
    • 2018-04-28
    • 1970-01-01
    • 2013-07-13
    相关资源
    最近更新 更多