Golang基础笔记十五之sync
Golang基础笔记十五之sync在并发编程的世界里Golang 的 goroutine 像一群不知疲倦的小精灵各自独立地执行任务。但当多个 goroutine 同时访问共享资源时就会引发数据竞争data race就像一群人同时抢一个麦克风场面就会失控。今天我们要聊的sync包就是 Golang 官方提供的“秩序维护者”它帮我们优雅地解决并发协作问题。### 为什么需要 sync先看一个反面教材。假设我们要统计一段文本中单词的总数用多个 goroutine 分别统计不同段落最后汇总。如果直接用一个共享变量累加会发生什么gopackage mainimport ( fmt runtime sync)func main() { var counter int var wg sync.WaitGroup // 模拟 10 个 goroutine 同时写 counter for i : 0; i 10; i { wg.Add(1) go func() { defer wg.Done() for j : 0; j 1000; j { counter // 这里就是数据竞争点 } }() } wg.Wait() fmt.Println(最终计数:, counter)}运行这段代码你会发现结果几乎不可能是 10000正确值。因为counter不是原子操作它分为“读取-修改-写入”三步多个 goroutine 同时执行时会互相覆盖对方的操作。此时sync包就派上用场了。### 一、sync.Mutex —— 互斥锁Mutex是最基础的同步原语它确保同一时刻只有一个 goroutine 能访问临界区。就像厕所的门锁谁拿到钥匙谁进入用完再还回来。gopackage mainimport ( fmt sync)func main() { var mu sync.Mutex // 定义互斥锁 var counter int var wg sync.WaitGroup for i : 0; i 10; i { wg.Add(1) go func() { defer wg.Done() for j : 0; j 1000; j { mu.Lock() // 加锁 counter // 临界区同一时间只有一个 goroutine 能执行这里 mu.Unlock() // 解锁 } }() } wg.Wait() fmt.Println(最终计数:, counter) // 确保输出 10000}关键点-Lock()和Unlock()必须成对出现否则会死锁。建议用defer确保解锁。- 锁的粒度要小避免长时间持有锁导致其他 goroutine 阻塞。### 二、sync.RWMutex —— 读写锁如果场景是“读多写少”比如缓存系统用Mutex会浪费性能——因为多个读操作其实可以并发进行。RWMutex提供了更细致的控制读锁可共享写锁独占。gopackage mainimport ( fmt sync time)type Cache struct { mu sync.RWMutex items map[string]int}func NewCache() *Cache { return Cache{items: make(map[string]int)}}// 读操作加读锁多个读者可同时进入func (c *Cache) Get(key string) (int, bool) { c.mu.RLock() defer c.mu.RUnlock() val, ok : c.items[key] return val, ok}// 写操作加写锁独占访问func (c *Cache) Set(key string, val int) { c.mu.Lock() defer c.mu.Unlock() c.items[key] val}func main() { cache : NewCache() cache.Set(apple, 5) var wg sync.WaitGroup // 模拟多个读者并发读 for i : 0; i 5; i { wg.Add(1) go func() { defer wg.Done() for j : 0; j 100; j { if v, ok : cache.Get(apple); ok v 5 { // 读操作成功 } } }() } wg.Wait() fmt.Println(并发读取完成)}注意读锁会阻塞写锁写锁会阻塞所有读锁。如果写操作特别频繁RWMutex可能反而更慢。### 三、sync.WaitGroup —— 等待组WaitGroup用于等待一组 goroutine 全部完成任务。它内部维护一个计数器Add(n)增加计数Done()减少计数Wait()阻塞直到计数归零。gopackage mainimport ( fmt sync time)func worker(id int) { defer fmt.Printf(worker %d 完成\n, id) time.Sleep(time.Second)}func main() { var wg sync.WaitGroup for i : 1; i 3; i { wg.Add(1) // 每启动一个 goroutine 就 1 go func(id int) { defer wg.Done() // 完成后 -1 worker(id) }(i) } wg.Wait() // 阻塞直到计数器为 0 fmt.Println(所有 worker 执行完毕)}易错点Add必须在go语句之前调用否则可能提前Wait结束。### 四、sync.Once —— 只执行一次Once用于确保某个函数在整个程序生命周期中只执行一次非常适合初始化全局资源如数据库连接。gopackage mainimport ( fmt sync)var ( configOnce sync.Once config string)func loadConfig() { configOnce.Do(func() { // 模拟耗时初始化 config 从配置文件加载的数据 fmt.Println(初始化完成) })}func main() { // 多个 goroutine 并发调用 loadConfig var wg sync.WaitGroup for i : 0; i 3; i { wg.Add(1) go func() { defer wg.Done() loadConfig() }() } wg.Wait() fmt.Println(最终配置:, config)}输出只有一次“初始化完成”说明Do内的代码只执行了一次。### 五、sync.Map —— 并发安全的 mapGo 内置的map不是并发安全的。sync.Map专门为“读多写少”的场景优化它内部使用了读写锁和原子操作性能通常优于“Mutex map”。gopackage mainimport ( fmt sync)func main() { var m sync.Map // 写入 m.Store(name, Alice) m.Store(age, 30) // 读取 if val, ok : m.Load(name); ok { fmt.Println(name:, val) } // 遍历 m.Range(func(key, value interface{}) bool { fmt.Printf(%v: %v\n, key, value) return true // 返回 false 可停止遍历 }) // 删除 m.Delete(age)}使用建议如果 key 是稳定的字符串且读多写少sync.Map很合适如果写操作频繁还是用Mutex map更简单可控。### 六、sync.Pool —— 对象池Pool用于缓存临时对象减少 GC 压力和分配开销。它适合存储“创建成本高、可复用”的对象如缓冲区。gopackage mainimport ( fmt sync)type Buffer struct { data []byte}func main() { pool : sync.Pool{ New: func() interface{} { return Buffer{data: make([]byte, 1024)} }, } // 获取对象 buf : pool.Get().(*Buffer) buf.data[0] A fmt.Println(使用中:, buf.data[0]) // 放回池中 pool.Put(buf) // 再次获取可能得到同一个对象 buf2 : pool.Get().(*Buffer) fmt.Println(再次获取:, buf2.data[0]) // 输出 A如果被复用了}注意Pool中的对象可能被 GC 清除不要依赖它做持久化存储。### 总结sync包是 Go 并发编程的基石它提供了从“互斥”到“协作”的完整工具链-Mutex / RWMutex解决资源竞争保护临界区。-WaitGroup等待一组任务完成实现“分而治之”。-Once保证只执行一次的初始化逻辑。-Map开箱即用的并发安全 map。-Pool对象复用提升性能。核心原则能用 channel 解决的并发问题优先用 channel必须共享内存时再考虑sync。而且锁的粒度要小避免死锁尽量用defer保证解锁。掌握这些工具你就能像指挥家一样让 goroutine 们和谐共舞。

相关新闻

Greasy Fork完整解决方案:实战指南与性能优化

Greasy Fork完整解决方案:实战指南与性能优化

Greasy Fork完整解决方案:实战指南与性能优化 【免费下载链接】greasyfork An online repository of user scripts. 项目地址: https://gitcode.com/gh_mirrors/gr/greasyfork 掌握浏览器定制化的核心技术,你将学会如何通过Greasy Fork的用户脚本…

2026/8/2 11:48:57阅读更多 →
GLM-OCR与Unity集成:实现游戏内动态文字识别与交互

GLM-OCR与Unity集成:实现游戏内动态文字识别与交互

1. 项目概述:当游戏世界“读懂”文字 在游戏开发中,我们常常会遇到一个看似简单却颇为棘手的需求:如何让游戏角色与场景中动态出现的文字内容进行交互?比如,一个解谜游戏里,墙上浮现出一段古老的咒语&#…

2026/8/2 11:48:57阅读更多 →
Unity与Unreal Engine双引擎30天入门:零基础实现首个3D游戏项目

Unity与Unreal Engine双引擎30天入门:零基础实现首个3D游戏项目

1. 项目概述:双引擎入门,为何选择与如何规划如果你对游戏开发感兴趣,刷到过Unity或Unreal Engine(虚幻引擎)那些令人惊叹的演示,心里痒痒但又觉得门槛太高,那这篇指南就是为你准备的。我见过太多…

2026/8/2 11:48:57阅读更多 →
数组从入门到精通:内存布局、核心操作与动态数组实战

数组从入门到精通:内存布局、核心操作与动态数组实战

1. 从“闯关”到“精通”:为什么数组实验值得你投入时间 最近在技术社区和在线学习平台上,看到不少朋友在讨论“头歌”这类在线实验平台的数组闯关题目。从热搜词里也能感受到大家的热情和困惑:从基础的“C语言数组和指针”、“数组常用方法”…

2026/8/2 13:24:19阅读更多 →
PDF转Word怕乱码?踩坑后总结的几种靠谱方法,一次讲清

PDF转Word怕乱码?踩坑后总结的几种靠谱方法,一次讲清

PDF转Word怕乱码?踩坑后总结的几种靠谱方法,一次讲清 上个月帮朋友改一份 30 多页的合作协议 PDF,对方没有给原始 Word 文档,只甩过来一个扫描版文件。我在手机上用微信搜了一个叫「青蓝PDF转换」的小程序,抱着试试看的…

2026/8/2 13:24:19阅读更多 →
STM32 GPIO操作:ODR、BSRR、BRR寄存器详解与原子操作实践

STM32 GPIO操作:ODR、BSRR、BRR寄存器详解与原子操作实践

1. 从一次“诡异”的IO口操作说起 最近在指导一位刚接触STM32的朋友调试一个简单的LED闪烁程序,遇到了一个让我会心一笑的“经典”问题。他的代码逻辑很简单:初始化一个GPIO引脚为推挽输出模式,然后在主循环里,先点亮LED&#xff…

2026/8/2 13:24:19阅读更多 →
i.MX6ULL嵌入式Linux开发:从设备树到应用层完整控制BEEP蜂鸣器

i.MX6ULL嵌入式Linux开发:从设备树到应用层完整控制BEEP蜂鸣器

如果你正在学习嵌入式Linux开发,特别是基于i.MX6ULL这样的ARM Cortex-A7平台,那么“如何控制一个简单的BEEP蜂鸣器”很可能是你遇到的第一个硬件交互实验。这个实验看似基础,却是一个绝佳的分水岭:它能让你清晰地看到,…

2026/8/2 13:24:19阅读更多 →
华为OD开发者一年实战:从技术融入、价值证明到职业发展全解析

华为OD开发者一年实战:从技术融入、价值证明到职业发展全解析

1. 从“OD”到“华为OD”:一个独特的身份标签 如果你在科技圈,尤其是国内的软件和互联网行业里待过一阵子,大概率听说过“华为OD”这个词。它不像“程序员”、“产品经理”那样是一个普适的职位名称,而是一个带有鲜明华为特色的、…

2026/8/2 13:24:19阅读更多 →
嵌入式串口通信全解析:从电平标准到协议设计的实战指南

嵌入式串口通信全解析:从电平标准到协议设计的实战指南

1. 项目概述:从“串口”这个老伙计说起干了这么多年嵌入式开发,调试过无数板子,串口(Serial)这个接口,可以说是我打交道最多、也最让我又爱又恨的一个“老伙计”了。爱它,是因为它简单、可靠&am…

2026/8/2 13:22:18阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:10阅读更多 →
限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

更多请点击: https://intelliparadigm.com 第一章:AI模板批量生成的核心价值与落地全景 AI模板批量生成正从实验性工具演进为现代软件工程的关键基础设施。它通过语义理解、上下文感知与结构化约束,将重复性高、模式明确的代码/文档/配置生成…

2026/8/2 0:00:12阅读更多 →
如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南 【免费下载链接】web-archives Browser extension for viewing archived and cached versions of web pages, available for Chrome, Edge and Safari 项目地址: https://gitcode.com/gh_mirrors/we/web-a…

2026/8/2 0:00:13阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:10阅读更多 →
限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

更多请点击: https://intelliparadigm.com 第一章:AI模板批量生成的核心价值与落地全景 AI模板批量生成正从实验性工具演进为现代软件工程的关键基础设施。它通过语义理解、上下文感知与结构化约束,将重复性高、模式明确的代码/文档/配置生成…

2026/8/2 0:00:12阅读更多 →
如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南 【免费下载链接】web-archives Browser extension for viewing archived and cached versions of web pages, available for Chrome, Edge and Safari 项目地址: https://gitcode.com/gh_mirrors/we/web-a…

2026/8/2 0:00:13阅读更多 →
无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理 【免费下载链接】lossless-cut The swiss army knife of lossless video/audio editing 项目地址: https://gitcode.com/gh_mirrors/lo/lossless-cut 在数字媒体创作领域,视频编辑处理的质量损…

2026/8/2 1:29:34阅读更多 →
AI辅助本科论文写作:8大工具评测与高效使用指南

AI辅助本科论文写作:8大工具评测与高效使用指南

1. 本科生论文写作的AI辅助现状本科毕业论文是每个大学生必须跨越的一道坎。记得我当年写论文时,光是文献检索就花了整整两周时间,打印的参考文献堆满了半个书桌。如今AI技术的发展为学术写作带来了革命性变化,合理使用这些工具可以节省80%以…

2026/8/2 2:32:55阅读更多 →
如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手 【免费下载链接】ticket-purchase 大麦自动抢票,支持人员、城市、日期场次、价格选择 项目地址: https://gitcode.com/GitHub_Trending/ti/ticket-purchase 还在为抢不到热门演唱会门票…

2026/8/2 2:09:20阅读更多 →