当前位置:首页 > 文章列表 > Golang > Go问答 > Go sync.Cond.Wait 为什么必须配合锁使用

Go sync.Cond.Wait 为什么必须配合锁使用

来源:17golang原创 2026-11-03 01:29:20 0浏览 收藏

Go 的 sync.Cond.Wait 必须在已经持有 c.L 的情况下调用。它会原子地释放这把锁并挂起当前 goroutine;被 Signal 或 Broadcast 唤醒后,返回前又会重新拿到锁。因此,Wait 不是“睡一会儿”,而是把“检查条件、释放锁、被唤醒、重新加锁”连接成一个同步边界。

官方文档:https://pkg.go.dev/sync#Cond.Wait

要点速览
  • 调用 Wait 前必须持有 Cond.L,否则条件检查和等待不在同一保护范围内。
  • 被唤醒只代表共享状态可能变化,返回后必须在 for 循环中重新检查条件。
  • 修改条件和发送通知应使用同一把锁;一次性通知场景通常可以直接用 channel。

Wait 先释放锁,返回前再把锁拿回来

sync.Cond 绑定一个 Locker。官方约定是:观察或修改条件时持有这把锁,调用 Wait 时也必须持有它。下面的最小例子展示了消费者和生产者共享的边界:

package main

import "sync"

var (
    mu    sync.Mutex
    cond  = sync.NewCond(&mu)
    ready bool
)

func waitReady() {
    cond.L.Lock()
    for !ready {
        // Wait 会原子释放 cond.L;返回前会重新持有它。
        cond.Wait()
    }
    // 这里仍然持有 cond.L,可以安全读取 ready 保护的数据。
    cond.L.Unlock()
}

func markReady() {
    cond.L.Lock()
    ready = true // 先修改共享条件,再通知等待者。
    cond.Broadcast()
    cond.L.Unlock()
}

如果只写 cond.Wait() 而没有先 Lock,就越过了 Cond.L 规定的使用边界。更重要的是,消费者会在没有保护的情况下观察 ready,生产者也可能在它检查之后、真正进入等待之前修改状态,最终让“已经满足的条件”没有被正确消费。

Go sync.Cond 中 Cond.L、共享条件、Wait 与唤醒通知的静态锁边界说明图
图1:结构说明图,展示 Cond.L、共享条件、Wait 和通知之间的静态关系,不是运行截图。

为什么 Wait 返回后还要用 for 再检查一次

Signal 和 Broadcast 的职责是唤醒等待者,不是替调用者证明条件为真。等待期间锁不在当前 goroutine 手里,其他 goroutine 可能先拿锁修改状态;多个消费者也可能竞争同一份资源。因此,返回后必须重新拿锁并回到条件判断。

type Gate struct {
    mu    sync.Mutex
    cond  *sync.Cond
    slots int
}

func NewGate() *Gate {
    g := &Gate{}
    g.cond = sync.NewCond(&g.mu)
    return g
}

func (g *Gate) Take() {
    g.mu.Lock()
    for g.slots == 0 {
        // 不能把一次唤醒当成一定有可用槽位。
        g.cond.Wait()
    }
    g.slots-- // 重新拿锁后再消费共享状态。
    g.mu.Unlock()
}

func (g *Gate) Release() {
    g.mu.Lock()
    g.slots++ // 状态先变为可用,再唤醒一个等待者。
    g.cond.Signal()
    g.mu.Unlock()
}

这里的 for 同时覆盖了两类情况:唤醒后槽位已经被另一个 goroutine 取走,以及通知发生时条件并没有按当前消费者需要的方式成立。把 if 改成 for,本质上是在每次真正使用共享数据前重新建立不变量。

Go sync.Cond 中共享条件、等待循环、Signal 与资源槽位的静态关系说明图
图2:关系说明图,展示条件判断、等待循环、Signal 和槽位状态的边界,不是运行结果。

状态修改和通知为什么要放在同一把锁附近

生产者可以在持锁时调用 Signal 或 Broadcast,也可以在某些场景下不持锁发送通知;但共享条件本身必须由同一同步边界保护。更容易维护的写法是:先持有 c.L,修改条件,发送通知,再解锁。这样读者能清楚看到条件的改变和唤醒动作属于同一次状态转移。

写法问题建议
Wait 前未持锁条件检查与等待脱节先 c.L.Lock()
用 if 包住 Wait返回后直接相信条件改为 for !condition()
条件由多把锁分别保护难以判断谁拥有状态统一使用 Cond.L
只是传一次值或广播结束Cond 结构偏重比较 channel 的发送或关闭

什么时候不该继续使用 sync.Cond

sync.Cond 适合多个 goroutine 围绕一个可变条件反复等待和唤醒,例如资源槽位、队列容量或阶段状态。若需求只是把一个值传给另一个 goroutine,使用 channel 更直观;如果只是通知“以后都不要再等”,关闭 channel 也能表达一次性广播。Go 官方文档同样提示,很多简单场景优先考虑 channel。

  • 看到 Wait,先问调用点是否持有 Cond.L。
  • 看到 Signal 或 Broadcast,再检查消费者是否用 for 重新判断条件。
  • 看到多个状态变量,确认它们是否由同一把锁保护并在同一边界内更新。

常见问题

Wait 会不会自己返回?

不会。官方文档说明它需要被 Signal 或 Broadcast 唤醒;但唤醒后仍要重新判断共享条件。

Signal 一定唤醒指定的 goroutine 吗?

不会。它唤醒一个等待者,但不提供等待者之间的调度优先级保证,所以条件判断不能依赖某个固定顺序。

调用 Broadcast 时必须持锁吗?

官方允许但不要求调用者持有 c.L。工程上更重要的是,条件修改与条件读取必须由同一把锁保护,保持状态转移容易理解。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go reflect.Value.SetString 何时要求目标值可设置Go reflect.Value.SetString 何时要求目标值可设置
上一篇
Go reflect.Value.SetString 何时要求目标值可设置
Go atomic 操作和普通读写混用为什么仍有数据竞争
下一篇
Go atomic 操作和普通读写混用为什么仍有数据竞争
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    850次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    853次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    934次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    909次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    737次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码