You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

未解锁的sync.Mutex为何会阻止http.ResponseWriter发送HTTP响应?

问题:注释defer解锁后首个请求为何无响应?

我知道应该用defer来解锁sync.Mutex,避免报错时未解锁导致其他goroutine阻塞,但搞不懂——如果注释掉h.dbMutex.Unlock(),为什么第一个拿到锁的goroutine连HTTP响应都返回不了?

代码示例:

type HandlerWrapper struct {
    db *sql.DB
    dbMutex *sync.Mutex
}
handlerWrapper := HandlerWrapper{db : db, dbMutex: &sync.Mutex{}}

// 使用go chi路由
router.Get("/items", handlerWrapper.GetAllHandler)

http.ListenAndServe(":5000", router)
func (h *HandlerWrapper) GetAllHandler(w http.ResponseWriter, r *http.Request){

    h.dbMutex.Lock()

    //defer h.dbMutex.Unlock() // 注释掉这行后,无HTTP响应,为什么?

    rows, err := h.db.Query("SELECT * FROM lst") // 这行故意返回错误
    if err != nil {
        log.Printf(": %v", err)
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
}

解答

首先明确:首个请求的goroutine其实已经生成并发送了响应,你觉得没收到大概率是测试场景的干扰,或者对goroutine和锁的关系理解有偏差,具体原因拆解如下:

  • 响应生成逻辑是正常执行的
    当db.Query报错后,代码会执行log.Printf打印错误,接着调用http.Error把错误信息和500状态码写入ResponseWriter,最后return结束goroutine。Go的HTTP服务器会在处理请求的goroutine退出时,自动把缓冲区里的响应内容发送给客户端,理论上客户端应该能收到响应。

  • 你误以为“无响应”的核心原因
    最常见的场景是:测试时同时发起了多个请求(比如浏览器开多个标签页、脚本并发请求)。首个请求的goroutine持有锁退出后,锁始终处于锁定状态,后续所有请求的goroutine都会在h.dbMutex.Lock()处阻塞。而客户端(比如Chrome)对同一个域名的并发连接数有限制(通常是6个),当后续请求占满连接数,首个请求的响应会被客户端延迟显示,让你误以为首个请求也没响应。

  • 锁未释放不会影响首个请求的响应发送
    sync.Mutex是你自定义的锁,和HTTP服务器的内部响应发送逻辑完全无关。持有锁的goroutine退出后,锁的状态会停留在“锁定”,但这只会阻塞后续请求,不会影响已经生成的响应发送。

  • 验证方式
    用curl单独发起一个请求,不要同时发起多个:

    curl http://localhost:5000/items
    

    这时候你应该能立刻收到500错误的响应,同时服务器日志也会打印对应的错误信息,以此证明首个请求的响应是正常发送的。


内容的提问来源于stack exchange,提问作者jmah27

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 13:30:38