Go并发编程:sync.WaitGroup的正确使用与并发安全解析


Go并发编程:sync.WaitGroup的正确使用与并发安全解析

本文深入探讨go语言中`sync.waitgroup`的正确使用方法,特别是`wg.add()`调用时机的关键性。我们将通过示例代码分析,解释为何`wg.add()`必须在`go`语句之前执行,以及go内存模型如何确保这一顺序,从而有效避免竞态条件和潜在的程序崩溃,保障并发程序的稳定运行。

在Go语言的并发编程中,`sync.WaitGroup`是一个至关重要的同步原语,用于等待一组goroutine完成其任务。它通过一个内部计数器来工作:`Add(delta int)`方法增加计数器,`Done()`方法(等价于`Add(-1)`)减少计数器,而`Wait()`方法则会阻塞,直到计数器归零。

`sync.WaitGroup`的基本用法

以下是一个典型的`sync.WaitGroup`使用示例,它启动了多个goroutine并行执行任务,并在所有任务完成后才继续主goroutine的执行:

package main
<p>import (
"fmt"
"sync"
"time"
)</p><p>func dosomething(millisecs time.Duration, wg <em>sync.WaitGroup) {
duration := millisecs </em> time.Millisecond
time.Sleep(duration)
fmt.Println("Function in background, duration:", duration)
wg.Done() // 任务完成后,减少计数器
}</p><p>func main() {
var wg sync.WaitGroup
wg.Add(4) // 预先告知WaitGroup将有4个goroutine需要等待
go dosomething(200, &wg)
go dosomething(400, &wg)
go dosomething(150, &wg)
go dosomething(600, &wg)</p><pre class="brush:php;toolbar:false;">wg.Wait() // 阻塞直到所有goroutine都调用了wg.Done()
fmt.Println("Done")

}

在这个例子中,`main`函数启动了四个`dosomething` goroutine。`wg.Add(4)`在所有`go`语句之前被调用,确保了`WaitGroup`的计数器在任何goroutine开始执行并可能调用`wg.Done()`之前就已经被正确设置。`wg.Wait()`则会等待这四个goroutine全部完成,即计数器归零,才会打印"Done"并退出。

`wg.Add()`调用时机的关键性

上述示例中,`wg.Add(4)`在所有`go`语句之前执行是至关重要的。虽然将`wg.Add(1)`分散到每个`go`语句之前也是正确的:

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go dosomething(200, &wg)
    wg.Add(1)
    go dosomething(400, &wg)
    wg.Add(1)
    go dosomething(150, &wg)
    wg.Add(1)
    go dosomething(600, &wg)
<pre class="brush:php;toolbar:false;">wg.Wait()
fmt.Println("Done")

}

但当已知goroutine数量时,一次性调用`wg.Add(N)`更为简洁高效。更重要的是,`wg.Add()`的调用必须在对应的`go`语句之前完成,以避免竞态条件和程序崩溃。

`WaitGroup`的内部计数器不能为负值。如果一个`wg.Done()`被调用时,计数器已经为零,程序将会发生`panic`。为了防止这种情况,我们必须确保每次`wg.Done()`被调用时,计数器都已经通过`wg.Add()`递增过。

《PHP程序设计》第二版 《PHP程序设计》第二版

本书图文并茂,详细讲解了使用LAMP(PHP)脚本语言开发动态Web程序的方法,如架设WAMP平台,安装与配置开源Moodle平台,PHP程序设计技术,开发用户注册与验证模块,架设LAMP平台。 本书适合计算机及其相关专业本、专科学生作为学习LAMP(PHP)程序设计或动态Web编程的教材使用,也适合对动态Web编程感兴趣的读者自觉使用,对LAMP(PHP)程序设计人员也具有一定的参考价值。

《PHP程序设计》第二版 713 查看详情 《PHP程序设计》第二版

Go内存模型与并发安全保障

Go语言的内存模型为我们提供了关于并发操作顺序的保证。理解这一点对于正确使用`sync.WaitGroup`至关重要:

  • 单个goroutine内的顺序: 在单个goroutine内部,所有语句的执行顺序看起来与它们在代码中出现的顺序一致。
  • `go`语句的发生顺序: Go内存模型明确指出,一个goroutine的执行不会在其对应的`go`语句完成之前开始。这意味着,在主goroutine中,`wg.Add()`语句的执行,保证在`go`语句启动的子goroutine开始执行其内部的`wg.Done()`之前。

因此,当我们在`go`语句之前调用`wg.Add()`时,Go的内存模型确保了以下顺序:

  1. 主goroutine执行`wg.Add(N)`,增加计数器。
  2. 主goroutine执行`go`语句,启动一个新的goroutine。
  3. 新的goroutine开始执行,并在完成任务后调用`wg.Done()`,减少计数器。

这种顺序保证了当子goroutine尝试调用`wg.Done()`时,`WaitGroup`的计数器已经被正确地初始化或递增,从而避免了计数器降至负数引发的`panic`。如果`wg.Add()`在`go`语句之后执行,就可能出现竞态条件:主goroutine可能在`wg.Add()`执行之前就启动了子goroutine,并且子goroutine可能在主goroutine执行`wg.Add()`之前就完成了任务并调用了`wg.Done()`,导致`panic`。

注意事项

  • `Add()`必须在`go`语句之前: 始终确保`wg.Add()`在启动任何可能调用`wg.Done()`的goroutine之前被调用。这是避免竞态条件和`panic`的核心规则。
  • `Done()`与`Add()`匹配: 每个`wg.Add(1)`或`wg.Add(N)`的增量都必须有对应的`wg.Done()`来抵消。通常,`wg.Done()`会放在`defer`语句中,以确保即使函数发生错误也能被调用,例如:
    func worker(id int, wg *sync.WaitGroup) {
        defer wg.Done() // 确保在函数退出时调用Done
        // ... 执行任务 ...
    }
  • 避免在被等待的goroutine中调用`Wait()`: 不要在被`WaitGroup`等待的goroutine内部调用同一个`WaitGroup`的`Wait()`方法,这会导致死锁。`Wait()`通常由主goroutine或协调goroutine调用。

总结

`sync.WaitGroup`是Go语言中实现并发协作的重要工具。正确理解和运用其`Add()`、`Done()`和`Wait()`方法,特别是`wg.Add()`的调用时机,是编写健壮、高效并发程序的关键。通过遵循Go内存模型的保证,确保`wg.Add()`在`go`语句之前执行,我们可以有效地避免竞态条件和运行时错误,从而构建出更加稳定可靠的并发应用。

以上就是Go并发编程:sync.WaitGroup的正确使用与并发安全解析的详细内容,更多请关注其它相关文章!


# go语言  # 企业如何优化网站平台  # 则会  # 死锁  # 能在  # 并在  # 本书  # 至关重要  # 器中  # 是一个  # 程序设计  # 并发编程  # ai  # 工具  # go  # 前就  # 对于双12网络营销推广  # 调查问卷网站建设方案  # 吹灰器网站建设优势  # 番禺网站建设策划内容  # 营销广告推广方式有几种  # 江苏定制网站优化  # 齐齐哈尔关键词排名电话  # 网站如何内部优化  # 赤峰抖音seo咨询 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: uc浏览器官网网页版使用 uc浏览器官网免费在线首页  mysql如何管理数据库账户_mysql数据库账户管理技巧  向往的生活小游戏启动处_向往的生活小游戏立即启动  解决Windows上Composer PATH变量冲突导致的命令无法识别问题  word表格如何按某一列内容进行排序_Word表格按列排序方法  如何查找哪个composer包引入了特定的依赖?  小红书网页版在线直达 小红书网页版免费登录入口  mysql如何限制远程访问_mysql远程访问限制方法  iPhone14开启Apple TV遥控设置  TikTok网页版入口快速访问 TikTok官网账号登录方法  《优志愿》修改手机号方法  智慧团建活动报名入口 智慧团建活动报名入口手机端官网​  VS Code如何设置默认配置  顺丰快递收费标准查询_如何查看顺丰最新收费价格  C++怎么解决数值计算中的精度问题_C++浮点数误差与数值稳定性分析  《七读免费小说》开通会员方法  《淘票票》添加到苹果钱包教程  b站怎么查看视频的码率_b站视频码率查看方法  铁路12306官网登录入口 铁路12306在线购票官方平台  《伊瑟》凶影追缉库卢鲁boss攻略  学习通网页版个人登录_学习通网页版个人账户登录入口  《领英》查看屏蔽名单方法  Flexbox布局中Stencil组件宽度不显示问题解析与:host尺寸控制  firefox火狐浏览器最新官网主页_ firefox火狐浏览器平台入口直达官方链接  《edge浏览器》关闭翻译功能方法  Win10关闭UAC用户账户控制的方法 Win10降低安全提示等级【技巧】  哔哩哔哩的|直播|间怎么送礼物_哔哩哔哩|直播|送礼操作指南  WooCommerce 新客户订单自动添加管理员备注教程  苹果电脑如何快速截图并编辑 苹果电脑截屏标注快捷操作  Leaflet地图弹出窗口图片动态显示:避免缺失图标的专业指南  《环球网校》设置报考省市方法  消除网页顶部意外空白线:CSS布局常见问题与解决方案  快递物流路径揭秘  mysql离线安装后如何启动_mysql离线安装完成后启动服务的方法  使用逻辑应用(Logic Apps)自动处理邮件附件中的XML到Excel  圆通快递包裹轨迹查询 圆通速递快件实时位置跟踪  荣耀Magic7拍照夜景噪点处理_荣耀Magic7相机优化  《procreate》绘制渐变效果教程  word邮件合并怎么插入个性化图片_Word邮件合并插入个性化图片方法  Windows自带的便笺数据如何备份_防止数据丢失的便利贴迁移教程【干货】  邦丰播放器频道搜索设置  《绝区零》2.3前瞻|直播|内容介绍  漫蛙漫画官方版直通入口 2025漫蛙漫画免注册访问说明  如何快速去除厨房重油污? 2025年最好用的厨房清洁剂推荐  使用VS Code调试Python代码:从入门到精通  解决CSS容器溢出问题:使用calc()实现精确布局与边距控制  《全民k歌》网页版最新登录入口一览  Go Template中优雅处理循环最后一项:自定义函数实践  《崩坏:星穹铁道》3.6版本异相仲裁打法及配队推荐  繁花漫画使用教程 

 2025-11-02

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.