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 们和谐共舞。

相关新闻

m4s-converter:3分钟拯救你的B站缓存视频,告别“数字孤岛“危机!

m4s-converter:3分钟拯救你的B站缓存视频,告别“数字孤岛“危机!

m4s-converter:3分钟拯救你的B站缓存视频,告别"数字孤岛"危机! 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-con…

2026/10/20 14:34:10 阅读更多 →
微软AI投资策略解析:从Anthropic收益与OpenAI减记看开发者技术选型

微软AI投资策略解析:从Anthropic收益与OpenAI减记看开发者技术选型

这次我们来看一个关于微软在AI领域投资策略的深度分析。根据最新披露的财务数据,微软在2024财年第三季度财报中,对AI初创公司Anthropic的投资实现了高达32亿美元的单季收益,而与此同时,对另一家巨头OpenAI的投资则进行了6亿美元的…

2026/10/19 20:56:55 阅读更多 →
从保研三十折到直博上岸:网络安全方向申请策略与面试复盘

从保研三十折到直博上岸:网络安全方向申请策略与面试复盘

1. 从“三十折”到“直博上岸”:一场非典型保研的复盘 2022年的夏天,当身边同学陆续晒出录取通知时,我的手机里还躺着十几封不同高校的“感谢参与”邮件。那段时间,我几乎把“保研”这个流程的每一个环节都踩了一遍坑,…

2026/10/20 7:20:52 阅读更多 →

最新新闻

自托管AI代码助手Tabby部署实践:内网环境下的开源补全方案

自托管AI代码助手Tabby部署实践:内网环境下的开源补全方案

开头就直说吧:GitHub Copilot 用起来确实爽,但我在实际项目里越用越觉得别扭——不是补全质量的问题,而是有一部分项目从一开始就注定和在线代码助手无缘。合规和安全规范摆在那里,源代码就是不能离开内网,哪怕只是把代…

2026/10/21 4:53:56 阅读更多 →
大模型智能体应用工程师:程序员转型指南与实战

大模型智能体应用工程师:程序员转型指南与实战

我认识的一位后端同事,上个月把做了五年的订单系统重构经验写成了简历,投了一圈之后发现,企业问得最多的不是分布式事务怎么做,而是“你会不会把大模型接进业务流程里”。这不是个案。2026年你要问程序员哪个方向给的钱最多&#…

2026/10/21 4:53:56 阅读更多 →
多Agent协作实战:用OpenClaw管理5个AI员工的完整指南

多Agent协作实战:用OpenClaw管理5个AI员工的完整指南

前阵子我在整理工作流程时,有个很实际的痛点:手头要处理的事情太多,单靠一个AI助手来回对话,效率并不高。你要先跟它讲一遍需求,它给你一个结果,你再复制粘贴到另一个工具里,中间全是人工搬砖。…

2026/10/21 4:53:56 阅读更多 →
C++实战:用中断+DMA驱动反射内存,告别轮询实现微秒级同步

C++实战:用中断+DMA驱动反射内存,告别轮询实现微秒级同步

搞过分布式实时系统的人,多半被同一件事折磨过:多个节点的数据同步,延迟要求动不动就是微秒级,而常规以太网一个中断过来,几毫秒就过去了。我最早接触反射内存是在一个分布式仿真项目里,那时候项目组还是老…

2026/10/21 4:53:56 阅读更多 →
构建可信的大模型评测报告:可信度雷达图实战指南

构建可信的大模型评测报告:可信度雷达图实战指南

上个周五下午,我们团队内部开模型评审会,桌上摆着两份大模型测试报告。一位工程师信誓旦旦地说新版模型明显更强,因为总分比上一版高了1.2分;另一位工程师却直接泼冷水,说数学维度的通过率是假的,测试集里有…

2026/10/21 4:53:56 阅读更多 →
大模型推理优化与部署实战:从KV Cache、量化到服务压测

大模型推理优化与部署实战:从KV Cache、量化到服务压测

大语言模型能写诗、能写代码,可是一旦真实跑到线上去服务用户,推理优化与部署才是真正决定系统能不能撑住的那道关卡。很多团队花大力气训好了模型,结果一上线就暴露各种问题:单请求时延太高、并发上来直接OOM、一个长对话把显存吃…

2026/10/21 4:52:56 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/20 10:02:13 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/20 20:43:59 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 10:02:08 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/20 9:50:45 阅读更多 →