Golang 并发机制-5:详解syn包同步原语
并发性是现代软件开发的一个基本方面,Go(也称为Golang)为并发编程提供了一组健壮的工具。Go语言中用于管理并发性的重要包之一是“sync”包。在本文中,我们将概述“sync”包,并深入研究其最重要的同步原语之一:Wait
Groups.
sync 包概述
sync包是Go中的一个标准库包,为并发编程提供同步原语。它为开发人员提供了协调和同步程序的工具,确保安全有序地执行并发任务。sync包提供的一些关键同步原语包括Mutexes, RWMutexes, Cond, Wait Groups。
Wait Groups
Wait Group是由Go中的“sync”包提供的同步原语。它是一个简单但功能强大的工具,用于管理goroutine的同步,特别是当你希望等待一组goroutine在继续之前完成它们的任务时。
当有多个并发执行独立任务的goroutine,并且你需要确保它们在继续执行主程序之前都已完成执行时,等待组是有用的。

让我们通过一个代码示例来探索如何使用Wait Groups:
package mainimport ("fmt""sync""time"
)func worker(id int, wg *sync.WaitGroup) {defer wg.Done() // Decrement the Wait Group counter when donefmt.Printf("Worker %d is working\n", id)time.Sleep(time.Second)fmt.Printf("Worker %d has finished\n", id)
}func main() {var wg sync.WaitGroupfor i := 1; i <= 3; i++ {wg.Add(1) // Increment the Wait Group counter for each Goroutinego worker(i, &wg)}wg.Wait() // Wait for all Goroutines to finishfmt.Println("All workers have finished.")
}
在这个例子中,我们定义了一个worker函数,它通过睡眠一秒钟来模拟工作。我们启动三个goroutine,每个代表一个worker,并使用sync。来协调他们的执行。
wg.Add(1)在启动每个例程之前增加等待组计数器。wg.Done()在worker函数中被延迟,以在gooutine完成其工作时减少计数器。wg.Wait()阻塞主程序,直到所有的例程都完成,确保我们等待所有工人的完成。
RWMutex
RWMutex(读写互斥)是Go语言中的一个同步原语,它允许多个线程同时读取共享数据,同时确保对写入的独占访问。它在经常读取数据但不经常修改数据的场景中非常有用。
下面是一个演示如何使用RWMutex的简单示例:
package mainimport ("fmt""sync""time"
)var (data intdataMutex sync.RWMutex
)func readData() int {dataMutex.RLock() // Read Lockdefer dataMutex.RUnlock()return data
}func writeData(value int) {dataMutex.Lock() // Write Lockdefer dataMutex.Unlock()data = value
}func main() {// Read data concurrentlyfor i := 1; i <= 5; i++ {go func() {fmt.Println("Read Data:", readData())}()}// Write datawriteData(42)time.Sleep(time.Second)
}
在这个例子中,多个Goroutine并发地读取共享的“数据”,并且一个单独的Goroutine写入它。RWMutex确保多个读取器可以同时访问数据,但一次只有一个写入器可以修改数据。
什么是条件变量?
在 Go 语言里,条件变量(sync.Cond)是一种同步原语,它用于协调多个 goroutine 的执行顺序,尤其是在某个条件满足时唤醒等待的 goroutine。条件变量通常和互斥锁(sync.Mutex 或 sync.RWMutex)一起使用,互斥锁用于保护共享资源,而条件变量则用于在共享资源的状态发生变化时通知等待的 goroutine。
主要特性
1. 等待(Wait)
Cond.Wait() 方法会让当前 goroutine 进入等待状态,并且会自动释放与之关联的互斥锁。当其他 goroutine 调用 Cond.Signal() 或 Cond.Broadcast() 唤醒它时,该 goroutine 会重新获取互斥锁并继续执行。
2. 单发通知(Signal)
Cond.Signal() 方法会唤醒一个正在等待该条件变量的 goroutine。如果有多个 goroutine 在等待,它会选择其中一个进行唤醒。
3. 广播通知(Broadcast)
Cond.Broadcast() 方法会唤醒所有正在等待该条件变量的 goroutine。
条件变量(Condition Variables)是同步原语,它允许程序在继续之前等待特定条件变为真。当你需要根据特定条件协调多个goroutine的执行时,它们很有帮助。
下面是一个说明条件变量使用的基本示例:
package mainimport ("fmt""sync""time"
)var (conditionMutex sync.Mutexcondition *sync.CondisReady bool
)func waitForCondition() {conditionMutex.Lock()defer conditionMutex.Unlock()for !isReady {fmt.Println("Waiting for the condition...")condition.Wait()}fmt.Println("Condition met, proceeding.")
}func setCondition() {time.Sleep(2 * time.Second)conditionMutex.Lock()isReady = truecondition.Signal() // Signal one waiting GoroutineconditionMutex.Unlock()
}func main() {condition = sync.NewCond(&conditionMutex)go waitForCondition()go setCondition()time.Sleep(5 * time.Second)
}
在这个例子中,一个Goroutine使用condition. wait()等待条件变为真,而另一个Goroutine将条件设置为true,并使用condition. signal()向等待的Goroutine发出信号。
下面是一个简单的示例,模拟生产者 - 消费者模型,使用条件变量来协调生产者和消费者的行为:
package mainimport ("fmt""sync""time"
)// 定义一个缓冲区和相关的锁与条件变量
var (buffer []intbufferLen = 5mutex sync.Mutexcond = sync.NewCond(&mutex)
)// 生产者函数
func producer(id int) {for {mutex.Lock()// 检查缓冲区是否已满for len(buffer) == bufferLen {fmt.Printf("Producer %d is waiting as buffer is full...\n", id)cond.Wait() // 缓冲区满,等待消费者消费}// 生产一个元素item := len(buffer) + 1buffer = append(buffer, item)fmt.Printf("Producer %d produced item %d. Buffer: %v\n", id, item, buffer)cond.Signal() // 通知可能正在等待的消费者mutex.Unlock()time.Sleep(time.Second)}
}// 消费者函数
func consumer(id int) {for {mutex.Lock()// 检查缓冲区是否为空for len(buffer) == 0 {fmt.Printf("Consumer %d is waiting as buffer is empty...\n", id)cond.Wait() // 缓冲区空,等待生产者生产}// 消费一个元素item := buffer[0]buffer = buffer[1:]fmt.Printf("Consumer %d consumed item %d. Buffer: %v\n", id, item, buffer)cond.Signal() // 通知可能正在等待的生产者mutex.Unlock()time.Sleep(time.Second)}
}func main() {// 启动生产者和消费者 goroutinego producer(1)go consumer(1)// 让程序运行一段时间time.Sleep(10 * time.Second)
}
通过使用条件变量,生产者和消费者能够在合适的时机进行等待和唤醒,确保缓冲区不会溢出或空消费。
原子操作
原子操作(Atomic Operations )是作为单个、不可分割的工作单元执行的操作。它们通常用于在不需要互斥锁的情况下安全地更新并发程序中的共享变量。Go为原子操作提供了一个名为“atomic”的包。
下面是演示原子操作的例子:
package mainimport ("fmt""sync""sync/atomic""time"
)var (counter int32wg sync.WaitGroup
)func incrementCounter() {defer wg.Done()for i := 0; i < 100000; i++ {atomic.AddInt32(&counter, 1)}
}func main() {wg.Add(2)go incrementCounter()go incrementCounter()wg.Wait()fmt.Println("Counter:", atomic.LoadInt32(&counter))
}
在这个例子中,两个线程使用原子操作增加共享的“计数器”变量。atomic.AddInt32函数确保增量操作是原子性的,并且对于并发访问是安全的。
选择正确的同步机制
在选择正确的同步机制时,请考虑以下指导原则:
- 当需要细粒度的访问控制时,互斥锁(RWMutex用于读,Mutex用于写)适用于保护共享数据。
- 当需要根据特定条件协调程序时,条件变量很有价值。
- 当希望避免互斥锁的开销时,原子操作对于对共享变量进行简单操作是有效的。
- 始终选择最适合您特定用例需求的同步机制。
总之,Go在“sync”包和原子操作中提供了一套通用的同步机制,用于管理对共享资源的并发访问。了解这些工具并根据您的并发需求选择合适的工具对于编写高效可靠的并发Go程序至关重要。
相关文章:
Golang 并发机制-5:详解syn包同步原语
并发性是现代软件开发的一个基本方面,Go(也称为Golang)为并发编程提供了一组健壮的工具。Go语言中用于管理并发性的重要包之一是“sync”包。在本文中,我们将概述“sync”包,并深入研究其最重要的同步原语之一…...
实验六 项目二 简易信号发生器的设计与实现 (HEU)
声明:代码部分使用了AI工具 实验六 综合考核 Quartus 18.0 FPGA 5CSXFC6D6F31C6N 1. 实验项目 要求利用硬件描述语言Verilog(或VHDL)、图形描述方式、IP核,结合数字系统设计方法,在Quartus开发环境下ÿ…...
如何用微信小程序写春联
生活没有模板,只需心灯一盏。 如果笑能让你释然,那就开怀一笑;如果哭能让你减压,那就让泪水流下来。如果沉默是金,那就不用解释;如果放下能更好地前行,就别再扛着。 一、引入 Vant UI 1、通过 npm 安装 npm i @vant/weapp -S --production 2、修改 app.json …...
LabVIEW无人机航线控制系统
介绍了一种无人机航线控制系统,该系统利用LabVIEW软件与MPU6050九轴传感器相结合,实现无人机飞行高度、速度、俯仰角和滚动角的实时监控。系统通过虚拟仪器技术,有效实现了数据的采集、处理及回放,极大提高了无人机航线的控制精度…...
C++哈希表深度解析:从原理到实现,全面掌握高效键值对存储
目录 一、核心组件与原理 1. 哈希函数(Hash Function) 2. 冲突解决(Collision Resolution) 3. 负载因子(Load Factor)与扩容 二、C实现:std::unordered_map 1. 模板参数 2. 关键操作与复…...
Vue.js组件开发-实现字母向上浮动
使用Vue实现字母向上浮动的效果 实现步骤 创建Vue项目:使用Vue CLI来创建一个新的Vue项目。定义组件结构:在组件的模板中,定义包含字母的元素。添加样式:使用CSS动画来实现字母向上浮动的效果。绑定动画类:在Vue组件…...
自研有限元软件与ANSYS精度对比-Bar2D2Node二维杆单元模型-四连杆实例
目录 1、四连杆工程实例以及手算求解 2、四连杆的自研有限元软件求解 2.1、选择单元类型 2.2、导入四连杆工程 2.3、节点坐标定义 2.4、单元连接关系、材料定义 2.5、约束定义 2.6、外载定义 2.7、矩阵求解 2.8、变形云图展示 2.9、节点位移 2.10、单元应力 2.11、…...
04树 + 堆 + 优先队列 + 图(D1_树(D11_伸展树))
目录 一、基本介绍 二、伸展操作 1. 左右情况的伸展 2. 左左情况的伸展 3. 右左情况的伸展 4. 右右情况的伸展 三、其它操作 1. 插入 2. 删除 四、代码实现 一、基本介绍 伸展树是一种二叉搜索树,伸展树也是一种平衡树,不过伸展树并不像AVL树那…...
c语言练习题【数据类型、递归、双向链表快速排序】
练习1:数据类型 请写出以下几个数据的数据类型 整数 a a 的地址 存放a的数组 b 存放a的地址的数组 b的地址 c的地址 指向 printf 函数的指针 d 存放 d的数组 整数 a 的类型 数据类型是 int a 的地址 数据类型是 int*(指向 int 类型的指针) …...
SliverAppBar的功能和用法
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了SliverGrid组件相关的内容,本章回中将介绍SliverAppBar组件.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在本章回中介绍的SliverAppBar和普通的AppBar类似,它们的…...
五、定时器实现呼吸灯
5.1 定时器与计数器简介 定时器是一种通过对内部时钟脉冲计数来测量时间间隔的模块。它的核心是一个递增或递减的寄存器(计数器值)。如果系统时钟为 1 MHz,定时器每 1 μs 计数一次。 计数器是一种对外部事件(如脉冲信号ÿ…...
Elasticsearch的索引生命周期管理
目录 说明零、参考一、ILM的基本概念二、ILM的实践步骤Elasticsearch ILM策略中的“最小年龄”是如何计算的?如何监控和调整Elasticsearch ILM策略的性能? 1. **监控性能**使用/_cat/thread_pool API基本请求格式请求特定线程池的信息响应内容 2. **调整…...
【大模型理论篇】最近大火的DeepSeek-R1初探系列1
1. 背景介绍 这一整个春节,被DeepSeek-R1刷屏。各种铺天盖地的新闻以及老板发的相关信息,着实感受到DeepSeek-R1在国外出圈的震撼。 DeepSeek推出了新的推理模型:DeepSeek-R1-Zero 和 DeepSeek-R1。DeepSeek-R1-Zero 是一个在没有经过监督微调…...
【数据结构】(4) 线性表 List
一、什么是线性表 线性表就是 n 个相同类型元素的有限序列,每一个元素只有一个前驱和后继(除了第一个和最后一个元素)。 数据结构中,常见的线性表有:顺序表、链表、栈、队列。 二、什么是 List List 是 Java 中的线性…...
【C++ STL】vector容器详解:从入门到精通
【C STL】vector容器详解:从入门到精通 摘要:本文深入讲解C STL中vector容器的使用方法,涵盖常用函数、代码示例及注意事项,助你快速掌握动态数组的核心操作! 一、vector概述 vector是C标准模板库(STL&am…...
OpenAI推出Deep Research带给我们怎样的启示
OpenAI 又发新产品了,这次是面向深度研究领域的智能体产品 ——「Deep Research」,貌似被逼无奈的节奏… 在技术方面,Deep Research搭载了优化后o3模型并通过端到端强化学习在多个领域的复杂浏览和推理任务上进行了训练。因没有更多的技术暴露…...
洛谷[USACO08DEC] Patting Heads S
题目传送门 题目难度:普及/提高一 题面翻译 今天是贝茜的生日,为了庆祝自己的生日,贝茜邀你来玩一个游戏。 贝茜让 N N N ( 1 ≤ N ≤ 1 0 5 1\leq N\leq 10^5 1≤N≤105) 头奶牛坐成一个圈。除了 1 1 1 号与 N N N 号奶牛外࿰…...
CSS 溢出内容处理:从基础到实战
CSS 溢出内容处理:从基础到实战 1. 什么是溢出?示例代码:默认溢出行为 2. 使用 overflow 属性控制溢出2.1 使用 overflow: hidden 裁剪内容示例代码:裁剪溢出内容 2.2 使用 overflow: scroll 显示滚动条示例代码:显示滚…...
Spring Boot项目如何使用MyBatis实现分页查询
写在前面:大家好!我是晴空๓。如果博客中有不足或者的错误的地方欢迎在评论区或者私信我指正,感谢大家的不吝赐教。我的唯一博客更新地址是:https://ac-fun.blog.csdn.net/。非常感谢大家的支持。一起加油,冲鸭&#x…...
飞行汽车中的无刷外转子电机、人形机器人中的无框力矩电机技术解析与应用
重点:无刷外转子电机与无框力矩电机:技术解析与应用对比 在现代工业自动化和精密机械领域,无刷电机因其高效、低噪音和高可靠性而备受青睐。其中,无刷外转子电机和无框力矩电机更是以其独特的结构和性能特点,成为众多应用场景中的…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
智能仓储的未来:自动化、AI与数据分析如何重塑物流中心
当仓库学会“思考”,物流的终极形态正在诞生 想象这样的场景: 凌晨3点,某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径;AI视觉系统在0.1秒内扫描包裹信息;数字孪生平台正模拟次日峰值流量压力…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
Spring是如何解决Bean的循环依赖:三级缓存机制
1、什么是 Bean 的循环依赖 在 Spring框架中,Bean 的循环依赖是指多个 Bean 之间互相持有对方引用,形成闭环依赖关系的现象。 多个 Bean 的依赖关系构成环形链路,例如: 双向依赖:Bean A 依赖 Bean B,同时 Bean B 也依赖 Bean A(A↔B)。链条循环: Bean A → Bean…...
基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
莫兰迪高级灰总结计划简约商务通用PPT模版
莫兰迪高级灰总结计划简约商务通用PPT模版,莫兰迪调色板清新简约工作汇报PPT模版,莫兰迪时尚风极简设计PPT模版,大学生毕业论文答辩PPT模版,莫兰迪配色总结计划简约商务通用PPT模版,莫兰迪商务汇报PPT模版,…...
