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

相关推荐
37手游后端团队几秒前
Cursor 工作区使用技巧:让 AI 真正理解你的多项目协作
后端·面试·架构
武子康2 分钟前
大数据-198 KNN 必须先归一化:Min-Max 正确姿势、数据泄露陷阱与 sklearn 落地
大数据·后端·机器学习
递归尽头是星辰4 分钟前
Elasticsearch实战:检索优化、聚合分析与架构落地体系化
大数据·elasticsearch·架构·检索优化·聚合分析
笙枫13 分钟前
LangGraph Agent 架构基础:从概念到第一个可运行的Agent
开发语言·架构·php
李拾叁的摸鱼日常13 分钟前
Java Optional 最佳实践+注意事项+避坑指南
java·后端·面试
啊哈灵机一动21 分钟前
手把手实现 Gin + Socket.IO 实时聊天功能
后端
qq_124987075321 分钟前
基于微信小程序的科技助农系统的设计与实现(源码+论文+部署+安装)
java·大数据·spring boot·后端·科技·微信小程序·毕业设计
狂奔小菜鸡22 分钟前
Day35 | Java多线程入门
java·后端·java ee
哈哈老师啊30 分钟前
Springboot新冠检测信息管理系统10m6v(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
数据库·spring boot·后端
华仔啊33 分钟前
ArrayList 和 LinkedList 的区别?一篇讲透,从此开发和面试都不再纠结
java·后端