Golang 并发原语 Sync Cond

Cond 条件变量

  • 主要是用于多个goroutine之间进行通知
  • 场景:等待某个事件的发生、通知某个事件发生

内部数据结构

go 复制代码
type Cond struct {
    noCopy noCopy

    // L is held while observing or changing the condition
    L Locker

    notify  notifyList
    checker copyChecker
}
  • noCopy 防止拷贝的一个标识

  • L 实现的一个互斥锁的接口

  • notify 阻塞链表

    go 复制代码
        type notifyList struct {
            wait   uint32 
            notify uint32
            lock   uintptr // key field of the mutex
            head   unsafe.Pointer
            tail   unsafe.Pointer
        }
    • wait 总需要等待的数量
    • notify 已经通知的数量
    • lock 阻塞链表的内部锁
    • head 链表头部指针
    • tail 链表尾部指针

核心方法

  • Wait:释放锁,挂起(阻塞)当前的goroutine,直到被唤醒,唤醒后再次获取锁

    go 复制代码
    func (c *Cond) Wait() {
        c.checker.check()
        t := runtime_notifyListAdd(&c.notify)
        c.L.Unlock()
        runtime_notifyListWait(&c.notify, t)
        c.L.Lock()
    }
    • 先进行一个check ckeck的操作,检查是否被copy
    • 将阻塞链表notifyList中的wait统计数+1,获取到一个"票"
    • 将当前goroutine包装成一个节点,添加到notifyList链表的尾部
    • 释放所(重点要考)
    • 调用系统内部gopark将当前goroutine挂起
    • 然后加锁。结束
    • 注意 :因为wait方法中先进行了释放锁的操作,所以在调用wait方法的时候一定要先进行加锁操作。不然会直接报panic
  • Singal:唤醒一个等待者

    go 复制代码
    func (c *Cond) Signal() {
        c.checker.check()
        runtime_notifyListNotifyOne(&c.notify)
    }
    • 依旧先进行一个check ckeck的操作,检查是否被copy
    • 阻塞链表notifyListnotify 统计数+1
    • 从头开始遍历阻塞链表,调用内部goready唤醒一个等待时间最长的goroutine
  • Broadcast:唤醒所有的等待者

    go 复制代码
    func (c *Cond) Broadcast() {
        c.checker.check()
        runtime_notifyListNotifyAll(&c.notify)
    }
    • 依旧先进行一个check ckeck的操作,检查是否被copy
    • wait赋值给notify
    • 唤醒所有阻塞链表中的节点

简单的使用方法:

go 复制代码
func Test_Cond(t *testing.T) {
    var c = sync.NewCond(new(sync.Mutex))
    var count int

    for i := 0; i < 10; i++ {
       go func(number int) {
          time.Sleep(1 * time.Second)
          c.L.Lock()
          count++
          c.L.Unlock()

          // 通知
          log.Printf("通知唤醒:i: %d count: %d", number, count)
          c.Signal()
       }(i)
    }

    c.L.Lock()
    for count != 10 {
       c.Wait()
       log.Printf("唤醒:count: %d", count)
    }
    c.L.Unlock()

    logx.Info("done")
}

大致简单的使用方法就是这样,一定要注意 在使用wait方法的时候一定要先进行加锁操作,还需要判断达成条件。

相关推荐
To_OC1 小时前
手写 AI 编程 Agent 的命令执行工具:我被 child_process 坑出来的实战经验
后端·node.js·agent
herosunly1 小时前
60ms 不是启动快,而是不用重新启动:CubeSandbox 极速冷启动架构拆解
架构·cube sandbox
zlinear数据采集卡2 小时前
从气动比例阀到任意波形:硬核拆解ZLinear采集卡的DAC输出架构与工业闭环控制实战
arm开发·架构
逝水无殇3 小时前
C# 异常处理详解
开发语言·后端·c#
2601_961946084 小时前
AI API 网关实战:从单 Key 管理到企业级多租户架构
大数据·人工智能·金融·架构·api·个人开发
考虑考虑6 小时前
Sentinel安装
java·后端·微服务
IT_陈寒6 小时前
SpringBoot自动配置失灵?你可能忘了这个关键注解
前端·人工智能·后端
碎碎念_4926 小时前
SpringBoot + Vue 前后端分离从 0 到 1 完整环境配置流程
vue.js·spring boot·后端
XUHUOJUN6 小时前
Windows 2025架构深度分析之Stretch Cluster 延迟工程现实
windows·microsoft·架构·azure local
逝水无殇6 小时前
C# 文件的输入与输出详解
开发语言·数据库·后端·c#