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

相关推荐
禁默10 分钟前
Rokid JSAR 技术开发全指南+实战演练
后端·restful·rokid·jsar
元气满满的霄霄17 分钟前
Spring Boot整合缓存——Redis缓存!超详细!
java·spring boot·redis·后端·缓存·intellij-idea
lypzcgf7 小时前
Coze源码分析-资源库-编辑工作流-后端源码-流程/技术/总结
go·源码分析·工作流·coze·coze源码分析·ai应用平台·agent平台
小蒜学长7 小时前
springboot多功能智能手机阅读APP设计与实现(代码+数据库+LW)
java·spring boot·后端·智能手机
kebeiovo7 小时前
muduo网络库事件驱动模型的实现与架构
网络·架构
追逐时光者8 小时前
精选 4 款开源免费、美观实用的 MAUI UI 组件库,助力轻松构建美观且功能丰富的应用程序!
后端·.net
你的人类朋友9 小时前
【Docker】说说卷挂载与绑定挂载
后端·docker·容器
间彧10 小时前
在高并发场景下,如何平衡QPS和TPS的监控资源消耗?
后端
间彧10 小时前
QPS和TPS的区别,在实际项目中,如何准确测量和监控QPS和TPS?
后端
间彧10 小时前
消息队列(RocketMQ、RabbitMQ、Kafka、ActiveMQ)对比与选型指南
后端·消息队列