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方法的时候一定要先进行加锁操作,还需要判断达成条件。

相关推荐
云雾J视界2 小时前
TMS320C6000 VLIW架构并行编程实战:加速AI边缘计算推理性能
人工智能·架构·边缘计算·dsp·vliw·tms320c6000
紫荆鱼2 小时前
设计模式-命令模式(Command)
c++·后端·设计模式·命令模式
编码追梦人2 小时前
深耕 Rust:核心技术解析、生态实践与高性能开发指南
开发语言·后端·rust
朝新_3 小时前
【SpringBoot】详解Maven的操作与配置
java·spring boot·笔记·后端·spring·maven·javaee
绝无仅有3 小时前
某教育大厂面试题解析:MySQL索引、Redis缓存、Dubbo负载均衡等
vue.js·后端·面试
消失的旧时光-19433 小时前
Flutter 异步体系终章:FutureBuilder 与 StreamBuilder 架构优化指南
flutter·架构
sean3 小时前
开发一个自己的 claude code
前端·后端·ai编程
ErizJ4 小时前
IM|im-service
golang·kafka·go·im·心跳检测
追逐时光者4 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 59 期(2025年10.20-10.26)
后端·.net
盖世英雄酱581365 小时前
java深度调试【第三章内存分析和堆内存设置】
java·后端