当前位置: 首页 > news >正文

Go语言的sync.Once和sync.Cond

一.sync.Once

Once(单次执行)

用途:确保某个操作只执行一次(如初始化配置)

核心方法:Do(f func()):保证 f只执行一次

package mainimport ("fmt""sync"
)var (config map[string]stringonce   sync.Oncewg     sync.WaitGroup
)func loadConfig() {once.Do(func() {fmt.Println("Loading config...")config = map[string]string{"key": "value"}})
}func main() {for i := 0; i < 5; i++ {wg.Add(1)go func() {defer wg.Done() // 确保 goroutine 结束时减少计数器loadConfig()}()}wg.Wait() // 等待所有 goroutine 完成
}

可以确保这个协程只进行一次

二.sync.Cond

sync.Cond 是 golang 标准库提供的并发协调器,用于支援开放人员在指定条件下阻塞和唤醒协程的操作.

  • Wait():释放锁并阻塞,直到被唤醒。
  • Signal():唤醒一个等待的 goroutine。
  • Broadcast():唤醒所有等待的 goroutine。

2.1 数据结构与构造器方法

type Cond struct {// 不可以对其进行值拷贝noCopy noCopy// 一个自旋锁L Locker// 一个队列,存放阻塞的goroutinenotify  notifyListchecker copyChecker
}// NewCond returns a new Cond with Locker l.
func NewCond(l Locker) *Cond {return &Cond{L:l}
}

(1)成员变量 noCopy + checker 是一套组合拳,保证 Cond 在第一次使用后不允许被复制;

(2)核心变量 L,一把锁,用于实现阻塞操作;

(3)核心变量 notify,阻塞链表,分别存储了调用 Cond.Wait() 方法的次数、goroutine 被唤醒的次数、一把系统运行时的互斥锁以及链表的头尾节点.

type notifyList struct {wait   uint32notify uint32lock   uintptr // key field of the mutexhead   unsafe.Pointertail   unsafe.Pointer
}

2.2 Cond.Wait

作用:把当前这个持有锁的goroutine,释放锁,陷入一个被动阻塞的状态,加入阻塞队列里面。

什么时候被唤醒呢?

当有其他的goroutine也持有这个Cond的引用,使用Signal函数的时候,会首先唤醒队首的goroutine

通过这个机制就可以实现异步goroutine的协调,比如需要某一个goroutine实现了某一个动作,另外一个goroutine才可以继续执行的一个场景。

使用的前置条件

看下面的代码我们会发现,它有一个解锁的操作,所以在调用他之前,必须是加锁的状态,只有这样才可以执行,然后陷入被动阻塞的状态,阻塞唤醒之后,才会重新加锁。

func (c *Cond) Wait() {c.checker.check()t := runtime_notifyListAdd(&c.notify)c.L.Unlock()runtime_notifyListWait(&c.notify, t)c.L.Lock()
}

(1)检查 Cond 是否在使用过后被拷贝,是则 panic;

(2)该 Cond 阻塞链表 wait 统计数加 1;

(3)当前协程释放锁,因为接下来即将被操作系统 park;

(4)将当前协程包装成节点,添加到 Cond 的阻塞队列当中,并调用 park 操作将当前协程挂起;

(5)协程被唤醒后,重新尝试获取锁.

2.3 Cond.Signal

作用:就是唤醒队首的goroutine唤醒

func (c *Cond) Signal() {c.checker.check()runtime_notifyListNotifyOne(&c.notify)
}

(1)检查 Cond 是否在首次使用后被拷贝,是则 panic;

(2)该 Cond 阻塞链表 notify 统计数加 1;

(3)从头开始遍历阻塞链表,唤醒一个等待时间最长的 goroutine.

2.4 Cond.BroadCast

作用:就是将阻塞队列里面的所有goroutine都进行唤醒。

func (c *Cond) Broadcast() {c.checker.check()runtime_notifyListNotifyAll(&c.notify)
}

(1)检查 Cond 是否在首次使用后被拷贝,是则 panic;

(2)取 wait 值赋值给 notify;

(3)唤醒阻塞链表所有节点.

2.5 使用案例

package mainimport ("fmt""sync""time"
)func main() {var mu sync.Mutexcond := sync.NewCond(&mu)// 共享状态isReady := false// 等待条件的goroutinego func() {fmt.Println("等待者: 等待条件满足...")cond.L.Lock()defer cond.L.Unlock()// 使用循环防止虚假唤醒for !isReady {cond.Wait() // 释放锁并阻塞,唤醒时会重新获得锁fmt.Println("等待者: 被唤醒,检查条件")}fmt.Println("等待者: 条件已满足!")}()// 改变条件的goroutinego func() {time.Sleep(2 * time.Second) // 模拟耗时操作fmt.Println("触发者: 准备改变条件...")cond.L.Lock()isReady = truecond.L.Unlock()fmt.Println("触发者: 发送通知")cond.Signal() // 唤醒一个等待的goroutine}()time.Sleep(3 * time.Second) // 等待所有goroutine完成
}
http://www.dtcms.com/a/265441.html

相关文章:

  • Redis 源码 tar 包安装 Redis 哨兵模式(Sentinel)
  • Go调度器的抢占机制:从协作式到异步抢占的演进之路|Go语言进阶(7)
  • 价值实证:数字化转型标杆案例深度解析
  • 网络地址与子网划分:一次性搞清 CIDR、VLSM 和子网掩码
  • 分类树查询性能优化:从 2 秒到 0.1 秒的技术蜕变之路
  • 如何在 IDEA 中设置类路径
  • 探索具身智能新高度——机器人在数据收集与学习策略中的优势和机会
  • Objective-C UI事件处理全解析
  • c++中的绑定器
  • 如何使用AI改进论文写作 ---- 引言篇(2)
  • 设计模式系列(10):结构型模式 - 桥接模式(Bridge)
  • AutoMedPrompt的技术,自动优化提示词
  • 【小技巧】Python + PyCharm 小智AI配置MCP接入点使用说明(内测)( PyInstaller打包成 .exe 可执行文件)
  • Spring Boot + 本地部署大模型实现:基于 Ollama 的集成实践
  • Jetson边缘计算主板:Ubuntu 环境配置 CUDA 与 cudNN 推理环境 + OpenCV 与 C++ 进行目标分类
  • 【Note】《深入理解Linux内核》Chapter 9 :深入理解 Linux 内核中的进程地址空间管理机制
  • MySQL数据库----DML语句
  • 深度学习新星:Mamba网络模型与核心模块深度解析
  • Python入门Day2
  • 【第三章:神经网络原理详解与Pytorch入门】01.神经网络算法理论详解与实践-(3)神经网络中的前向传播、反向传播的原理与实现
  • Python中`import` 语句的执行涉及多个步骤
  • 【Python】批量提取超声波检查图片的某一行数据
  • Docker 容器如何实现资源限制(如 CPU 和内存)
  • MacOS Safari 如何打开F12 开发者工具 Developer Tools
  • 【C++】状态模式
  • 好用的自带AI功能的国产IDE
  • Go与Python爬虫对比及模板实现
  • 信刻光盘安全隔离与文件单向导入/导出系统
  • 高压电缆护层安全的智能防线:TLKS-PLGD 监控设备深度解析
  • NVIDIA Spectrum-3 SN4000 系列SN4000 SN4000 系列速度高达 400Gb/秒的现代横向扩展分布式数据中心应用提供支持。