Go 语言并发编程的 “工具箱” 📅 发布时间:2026/7/12 12:12:27 👁️ 浏览次数: Go 语言并发编程的 “工具箱”sync包是 Go 语言并发编程的 “工具箱”里面的每一个 API 都是为了解决特定的并发问题设计的。基础协作WaitGroup等待组作用等待一组 goroutine 全部执行完毕是最常用的 “并发等待” 工具。原理内部是一个计数器Add(n)增加计数Done()减少计数Wait()阻塞直到计数归零。packagemainimport(fmtsynctime)funcmain(){varwg sync.WaitGroup// 启动 3 个 goroutinefori:1;i3;i{wg.Add(1)// 启动前加 1gofunc(idint){deferwg.Done()// goroutine 结束前减 1fmt.Printf(Goroutine %d 开始工作\n,id)time.Sleep(1*time.Second)fmt.Printf(Goroutine %d 工作完成\n,id)}(i)}fmt.Println(主 goroutine 等待所有子 goroutine 完成...)wg.Wait()// 阻塞直到计数归零fmt.Println(所有工作完成)}适用场景并发批量处理任务如并发下载 10 个文件、并发查询 5 个数据库主 goroutine 需要等所有子 goroutine 干完活再继续执行。互斥锁Mutex互斥锁作用保证同一时间只有一个 goroutine访问共享资源防止 “竞态条件”。原理Lock()加锁Unlock()解锁加锁后其他 goroutine 必须等解锁后才能加锁。packagemainimport(fmtsync)var(countintmu sync.Mutex)funcincrement(){mu.Lock()// 加锁锁住共享资源 countdefermu.Unlock()// 函数结束前解锁count}funcmain(){varwg sync.WaitGroupfori:0;i1000;i{wg.Add(1)gofunc(){deferwg.Done()increment()}()}wg.Wait()fmt.Println(最终 count 值,count)// 不加锁会小于 1000加锁后一定是 1000}适用场景多个 goroutine 同时修改同一个共享变量如全局计数器、共享缓存任何需要 “独占访问” 共享资源的场景。读写锁RWMutex读写互斥锁作用Mutex的升级版适用于读多写少的场景 —— 读操作可以并发写操作必须独占。原理读锁RLock()/RUnlock()多个 goroutine 可以同时加读锁读与读不互斥写锁Lock()/Unlock()写锁与读锁、写锁与写锁都互斥写操作时不能读也不能写。packagemainimport(fmtsynctime)var(datamap[string]stringrwMu sync.RWMutex)// 读操作加读锁多个 goroutine 可以并发读funcreadData(keystring){rwMu.RLock()// 加读锁deferrwMu.RUnlock()// 解读锁fmt.Printf(读取 %s: %s\n,key,data[key])time.Sleep(100*time.Millisecond)// 模拟读耗时}// 写操作加写锁独占访问funcwriteData(key,valuestring){rwMu.Lock()// 加写锁deferrwMu.Unlock()// 解写锁fmt.Printf(写入 %s: %s\n,key,value)data[key]value time.Sleep(500*time.Millisecond)// 模拟写耗时}funcmain(){datamake(map[string]string)varwg sync.WaitGroup// 启动 5 个读 goroutine可以并发fori:1;i5;i{wg.Add(1)gofunc(idint){deferwg.Done()readData(fmt.Sprintf(key%d,id%2))// 调度 0 与 1}(i)}// 启动 1 个写 goroutine会阻塞所有读wg.Add(1)gofunc(){deferwg.Done()writeData(key1,value1)}()wg.Wait()/* 写入 key1: value1 读取 key1: value1 读取 key0: 读取 key0: 读取 key1: value1 读取 key1: value1 写 goroutine 抢到了锁先执行了写入 ( goroutine 启动顺序 不保证先读后写。) 写锁释放后所有读 goroutine 才开始执行 读 goroutine 读取 key1 时已经被写 goroutine 写成 value1 读 goroutine 读取 key0 时map 中没有这个 key所以打印空字符串 */}适用场景读多写少的共享资源如配置文件、缓存数据需要区分 “读并发” 和 “写独占” 的场景比Mutex性能更好。单次执行Once作用保证某个函数只执行一次即使在并发场景下也如此。原理内部用Mutex 原子变量实现Do(f)只会调用f一次。packagemainimport(fmtsync)typeConfigstruct{Envstring}var(config*Config once sync.Once)// 初始化配置只会执行一次funcinitConfig(){fmt.Println(初始化配置只会执行一次)configConfig{Env:dev}}// 获取配置并发安全的单例funcGetConfig()*Config{once.Do(initConfig)// 保证 initConfig 只执行一次returnconfig}funcmain(){varwg sync.WaitGroupfori:0;i5;i{wg.Add(1)gofunc(){deferwg.Done()cfg:GetConfig()fmt.Printf(获取到配置%v\n,cfg)}()}wg.Wait()/* 初始化配置只会执行一次 获取到配置{dev} 获取到配置{dev} 获取到配置{dev} 获取到配置{dev} 获取到配置{dev} */}适用场景单例模式如全局配置、数据库连接池初始化只需要执行一次的初始化操作如加载配置文件、注册服务。并发 MapMap作用并发安全的 Map普通 Map 并发读写会 panicsync.Map不需要加锁就能并发读写。核心 APIStore(key, value)存键值对Load(key)取键值对Delete(key)删键值对Range(f func(key, value interface{}) bool)遍历所有键值对。packagemainimport(fmtsync)funcmain(){varm sync.Map// 1. 并发写入varwg sync.WaitGroupfori:1;i5;i{wg.Add(1)gofunc(idint){deferwg.Done()key:fmt.Sprintf(key%d,id)m.Store(key,fmt.Sprintf(value%d,id))fmt.Printf(写入 %s\n,key)}(i)}wg.Wait()// 2. 读取单个值ifval,ok:m.Load(key3);ok{fmt.Printf(读取 key3: %s\n,val)}// 3. 遍历所有键值对fmt.Println(遍历所有键值对)m.Range(func(key,valueinterface{})bool{fmt.Printf(%s: %s\n,key,value)returntrue// 返回 true 继续遍历false 停止})}适用场景并发缓存如热点数据缓存、Session 存储多个 goroutine 同时读写的 Map避免自己加锁的麻烦注意sync.Map适用于 “读多写少” 且 “键值对相对稳定” 的场景写多读少的场景性能不如 “普通 Map Mutex”。对象池Pool作用复用对象减少内存分配和 GC垃圾回收压力提升性能。原理内部是一个 “池子”Get()从池子里取对象Put()把用完的对象放回池子如果池子空了会调用New函数创建新对象。packagemainimport(fmtsync)// 模拟一个需要频繁创建的对象比如缓冲区typeBufferstruct{data[]byte}funcmain(){// 1. 定义对象池pool:sync.Pool{New:func()interface{}{fmt.Println(创建新的 Buffer 对象)returnBuffer{data:make([]byte,1024)}},}// 2. 第一次获取池子空了调用 New 创建buf1:pool.Get().(*Buffer)// 判断 buf1 是否为空ifbuf1nil||buf1.datanil{fmt.Println(buf1 是空对象)}else{fmt.Println(buf1 是有效对象)}// buf1 是有效对象// 3. 用完放回池子pool.Put(buf1)fmt.Println(buf1 放回池子)// 4. 第二次获取直接从池子取不创建新对象buf2:pool.Get().(*Buffer)// 判断 buf2 是否与 buf1 是同一个对象ifbuf1buf2{fmt.Println(buf2 与 buf1 是同一个对象)}else{fmt.Println(buf2 与 buf1 不是同一个对象)}// buf2 与 buf1 是同一个对象}适用场景:频繁创建和销毁的大对象如数据库连接、HTTP 连接、大缓冲区需要减少 GC 压力的高性能场景如高并发 Web 服务、消息队列。复用大对象最常用bytes.Buffer、[]byte切片、大型的自定义Struct。解析一个巨大的 JSON 时你需要一个临时缓存区。用完还给Pool下一个请求接着用。数据库连接的辅助注意不是连接池本身虽然它不能当数据库连接池用但可以用来存放用于构建查询语句的临时字符串缓冲。Web 框架上下文比如Gin 框架。每一个 HTTP 请求进来Gin 都会创建一个Context对象。Gin 并没有每次都new一个而是从sync.Pool里取。请求结束放回池子。条件变量Cond作用协调多个 goroutine 之间的 “等待 - 通知” 逻辑比 channel 更灵活比如可以一次性通知所有等待的 goroutine。原理Wait()释放锁并阻塞等待被通知后重新加锁Signal()通知一个等待的 goroutineBroadcast()通知所有等待的 goroutine必须和Mutex一起用。// 生产者 - 消费者模式packagemainimport(fmtsynctime)funcmain(){varmu sync.Mutex cond:sync.NewCond(mu)// 用 Mutex 创建 Condvarqueue[]int// 共享队列// 生产者往队列里放数据producer:func(){fori:1;i3;i{mu.Lock()queueappend(queue,i)fmt.Printf(生产了 %d队列长度%d\n,i,len(queue))cond.Signal()// 通知一个消费者mu.Unlock()time.Sleep(500*time.Millisecond)}}// 消费者从队列里取数据consumer:func(idint){for{mu.Lock()// 队列空了就等待forlen(queue)0{cond.Wait()// 释放锁并阻塞被通知后重新加锁}// 取数据item:queue[0]queuequeue[1:]fmt.Printf(消费者 %d 消费了 %d队列长度%d\n,id,item,len(queue))mu.Unlock()time.Sleep(1*time.Second)}}// 启动 1 个生产者2 个消费者goproducer()goconsumer(1)goconsumer(2)// 简单等待time.Sleep(5*time.Second)}适用场景:复杂的生产者 - 消费者模式需要灵活控制通知逻辑需要 “一次性通知所有等待 goroutine” 的场景如配置更新后通知所有 worker 重新加载Cond比较复杂能用 channel 解决的场景优先用 channelchannel 解决不了再用Cond。总结API核心作用一句话适用场景WaitGroup等待一组 goroutine 完成并发批量处理任务等所有任务干完再继续Mutex互斥锁独占访问共享资源多个 goroutine 同时修改同一个变量RWMutex读写锁读并发、写独占读多写少的共享资源如配置、缓存Once保证函数只执行一次单例模式、全局初始化Map并发安全的 Map多个 goroutine 同时读写的缓存Pool对象池复用对象频繁创建销毁的大对象减少 GCCond条件变量协调等待 - 通知复杂的生产者 - 消费者、需要广播通知的场景选择原则优先用简单的WaitGroupMutexRWMutexMapPoolCond读多写少优先考虑RWMutex锁或Map并发 Map性能优化频繁创建大对象时用Pool复杂协调channel 解决不了的场景再用Cond。
Wan2.2-T2V-A5B驱动智能Agent:自主规划与执行多步骤视频创作 Wan2.2-T2V-A5B驱动智能Agent:自主规划与执行多步骤视频创作 1. 引言:当视频创作遇上“智能大脑” 你有没有想过,有一天你只需要说一句“帮我做个介绍卷积神经网络的科普短片”,然后就能去喝杯咖啡,回来时一个结构完… 2026/7/11 2:10:09
树莓派4B UART通信避坑指南:如何正确配置硬件串口与mini串口 树莓派4B UART通信:从硬件串口到软件配置的深度实践 如果你正在用树莓派4B做嵌入式项目,尤其是需要和传感器、显示屏或者其他微控制器进行可靠的数据交换,那么UART(通用异步收发传输器)几乎是你绕不开的一环。但很多朋… 2026/7/10 15:38:59
Ubuntu18.04黑屏自救指南:从tty到图形界面的5种修复方案(附网络配置技巧) Ubuntu 18.04 黑屏自救指南:从tty到图形界面的5种修复方案(附网络配置技巧) 那天下午,我正打算清理一下虚拟机里那个积灰已久的Ubuntu 18.04开发环境,手一滑,执行了一个看似无害的sudo apt-get remove --au… 2026/7/10 22:34:08
面向无人机与水面设备的微塑料YOLO检测数据集(含多光照、多视角真实海洋样本) 本文还有配套的精品资源,点击获取 简介:1154张真实海洋场景图像,覆盖无人机高空航拍和水面近景拍摄两种视角,包含不同浑浊度水体、晴天/阴天/反光等多种光照条件。所有图片已按YOLOv5/v7/v8通用格式标注,直接支持训… 2026/7/12 12:11:01
STC89C52电子密码锁工程:支持掉电存密、LCD实时显示、矩阵键盘输入与完整调试资源 本文还有配套的精品资源,点击获取 简介:基于STC89C52单片机的电子密码锁完整可运行工程,支持4位数字密码输入、三次错误锁定、密码修改功能;密码数据掉电后自动保存至AT24C02 EEPROM芯片,通过标准I2C协议读写&#… 2026/7/12 12:09:00
直流有刷电机驱动系统设计与实现:H桥与PIC18F控制 1. 项目背景与核心器件选型在工业自动化和小型机电设备领域,直流有刷电机因其结构简单、控制方便、成本低廉等优势,仍然是许多应用场景的首选驱动方案。本次项目采用的TC78H651AFNGTOSHIBA H桥驱动器与Microchip PIC18F2610微控制器组合,构成… 2026/7/12 12:09:00
TMC7300与STM32L432KC实现高效直流电机控制方案 1. TMC7300与STM32L432KC电机控制方案概述有刷直流电机在工业自动化、消费电子和机器人领域广泛应用,但其控制稳定性一直是工程师面临的挑战。本文将详细介绍基于TMC7300电机驱动器和STM32L432KC微控制器的解决方案,该组合能够实现高效、精准的电机控制。… 2026/7/12 12:07:00
GPT-5.6 全面解读:三档模型、推理能力、价格与迁移实战 摘要 2026 年 7 月 9 日,OpenAI 正式推出 GPT-5.6 系列,并在 ChatGPT、Codex 与 API 中逐步开放。这次升级的重点并非单纯扩大参数或上下文窗口,而是通过 Sol、Terra、Luna 三档模型覆盖不同成本区间,再结合 max 推理强度、Pro 模… 2026/7/12 12:07:00
Zotero 7.0 跨平台同步实战:Windows/Mac/Linux 三端配置与 5 插件推荐 Zotero 7.0 跨平台同步实战:Windows/Mac/Linux 三端配置与 5 插件推荐在科研工作流中,文献管理工具的跨平台同步能力直接决定了研究效率的上限。当你在实验室的Windows工作站上收集了50篇最新论文,回家后想在MacBook上继续整理,第… 2026/7/12 12:04:59
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/12 0:01:13
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/12 0:01:13
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/12 0:03:14
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/12 0:01:13
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/12 0:01:13
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/12 0:03:14