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
    • 阻塞链表notifyList中notify 统计数+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方法的时候一定要先进行加锁操作,还需要判断达成条件。

相关推荐
代码方舟10 分钟前
零信任架构实战:基于天远人企关联构建自动化供应链核心企业关联穿透与利益冲突审查网关
java·人工智能·架构·自动化
奈落2420 分钟前
Dify 管对话、n8n 管执行:把‘大脑+手脚‘的组合架构对照三方平台资料读清楚
人工智能·架构·agent·ai 办公
qianbo_insist35 分钟前
# 在 Qwen3.5-2B 上把 GDN-1 无损换成 GDN-2:一次完整的架构手术实验(负结果)
人工智能·pytorch·python·架构
摸爬滚打的小李1 小时前
TomCat
后端
itmigrate1 小时前
Rust 里的所有权误区:不是不写 clone 就叫高性能
开发语言·后端·rust
码事漫谈1 小时前
Deepseek v5 来了
后端
爱勇宝1 小时前
你可能成不了技术大佬,然后呢?
前端·后端·程序员
JuiceFS1 小时前
HeyGen:如何让全球 10+ GPU 集群训练不再等待数据?
人工智能·后端
暧暧内含光2 小时前
Electron 桌面应用打包原理与实战避坑
后端
郭小李2 小时前
spring @PathVariable 注解用法
java·后端·spring