
本文深入探讨了Go语言中N个worker goroutine与一个监控goroutine协调时常见的死锁问题。通过分析`sync.WaitGroup`和通道(channel)的不当使用,文章提供了两种有效的解决方案:一是通过在所有worker完成后关闭通道,使接收方优雅退出;二是在打印逻辑也由单独goroutine处理时,引入额外的同步通道来确保主程序正确终止,从而避免`all goroutines are asleep - deadlock`。
在Go语言的并发编程中,goroutine、channel和sync.WaitGroup是实现高效并发模式的核心工具。然而,不恰当的使用方式,尤其是通道的生命周期管理,很容易导致程序进入死锁状态,并抛出all goroutines are asleep - deadlock的错误。本教程将通过一个经典的“生产者-消费者”场景,深入分析这类死锁的成因,并提供两种健壮的解决方案。
考虑一个常见的并发场景:有N个工作(worker)goroutine负责生产数据并发送到一个共享通道,一个监控(monitor)goroutine负责从该通道接收并处理数据,而主程序需要等待所有工作和监控任务完成后才退出。
以下是一个可能导致死锁的初始代码示例:
package main
import (
"fmt"
"strconv"
"sync"
)
func worker(wg *sync.WaitGroup, cs chan string, i int) {
defer wg.Done()
cs <- "worker" + strconv.Itoa(i)
}
func monitorWorker(wg *sync.WaitGroup, cs chan string) {
defer wg.Done()
for i := range cs { // 此处会无限等待
fmt.Println(i)
}
}
func main() {
wg := &sync.WaitGroup{}
cs := make(chan string)
// 启动10个worker goroutine
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(wg, cs, i)
}
// 启动一个monitorWorker goroutine
wg.Add(1)
go monitorWorker(wg, cs)
// 等待所有goroutine完成
wg.Wait()
}问题分析:
上述代码中,main函数启动了10个worker goroutine和一个monitorWorker goroutine。worker goroutine将数据发送到cs通道,并在完成后调用wg.Done()。monitorWorker goroutine通过for i := range cs循环从通道cs接收数据。
死锁发生的原因在于:
解决此问题的关键在于,当所有发送方完成任务后,必须关闭通道cs,以通知接收方monitorWorker(或main函数中的for range循环)不再有数据会到来,从而使其优雅地退出循环。
我们可以引入一个专门的monitorWorker goroutine来负责等待所有worker完成,然后关闭cs通道。而数据接收和打印的逻辑则可以放在main函数中。
Copymatic
Cowriter是一款AI写作工具,可以通过为你生成内容来帮助你加快写作速度和激发写作灵感。
149
查看详情
package main
import (
"fmt"
"strconv"
"sync"
)
func worker(wg *sync.WaitGroup, cs chan string, i int) {
defer wg.Done()
cs <- "worker" + strconv.Itoa(i)
}
// 新的monitorWorker职责:等待所有worker完成,然后关闭通道
func monitorWorker(wg *sync.WaitGroup, cs chan string) {
wg.Wait() // 等待所有worker goroutine完成
close(cs) // 所有worker完成后,关闭通道cs
}
func main() {
wg := &sync.WaitGroup{}
cs := make(chan string)
// 启动10个worker goroutine
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(wg, cs, i)
}
// 启动一个专门的monitorWorker来关闭通道
go monitorWorker(wg, cs) // 注意:这里不再将monitorWorker添加到wg中
// main goroutine负责从通道接收并打印数据
for i := range cs { // 当cs通道被关闭时,此循环将自动退出
fmt.Println(i)
}
// main函数在此处退出,程序结束
}方案分析:
注意事项:
如果业务需求坚持将数据接收和打印逻辑也放在一个独立的goroutine中(例如,printWorker),那么我们需要更复杂的同步机制来确保main函数在所有数据处理完毕后才退出。
package main
import (
"fmt"
"strconv"
"sync"
)
func worker(wg *sync.WaitGroup, cs chan string, i int) {
defer wg.Done()
cs <- "worker" + strconv.Itoa(i)
}
// 职责同解决方案一:等待所有worker完成,然后关闭通道
func monitorWorker(wg *sync.WaitGroup, cs chan string) {
wg.Wait()
close(cs)
}
// 独立的打印goroutine,负责从cs接收并打印数据
func printWorker(cs <-chan string, done chan<- bool) {
for i := range cs {
fmt.Println(i)
}
// 当cs通道关闭且所有数据处理完毕后,向done通道发送信号
done <- true
}
func main() {
wg := &sync.WaitGroup{}
cs := make(chan string)
// 启动10个worker goroutine
for i := 0; i < 10; i++ {
wg.Add(1)
go worker(wg, cs, i)
}
// 启动monitorWorker来关闭cs通道
go monitorWorker(wg, cs)
// 创建一个用于printWorker通知main完成的通道
done := make(chan bool, 1) // 使用带缓冲的通道,避免printWorker阻塞
// 启动printWorker goroutine
go printWorker(cs, done)
// main goroutine等待printWorker完成的信号
<-done // 阻塞直到从done通道接收到信号
}方案分析:
关键点:
解决Go并发编程中的死锁问题,特别是all goroutines are asleep - deadlock,核心在于对goroutine生命周期和通道状态的精确管理。
通过理解这些原则并应用上述解决方案,您可以有效地构建健壮、高效且无死锁的Go并发程序。
以上就是Go 并发模式:使用 WaitGroup 和通道避免死锁的详细内容,更多请关注其它相关文章!
# go语言
# go
# 达州移动网站建设
# 锐酷网站建设流程
# 巩义网站建设优化诊断
# seo排名影响
# 响水seo优化哪个好
# 怒江州网站推广费用
# 私募股权基金网站建设
# 两江游的市场营销和推广
# 合川抖音关键词排名公司
# seo网站怎么设置https
# 数据处理
# 两种
# 主程序
# 尤其是
# 放在
# 器中
# 都已
# 发送到
# 完成后
# 死锁
# 同步机制
# 并发编程
# ai
# 工具
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
小红书网页版在线直达 小红书网页版免费登录入口
iPhone12是否要更新ios16
抖音如何解除|直播|权限绑定_抖音关闭并解绑|直播|功能的方法
《i莞家》修改昵称方法
谷歌浏览器官网地址整理_谷歌浏览器新版直连2026稳定访问
《偃武》甘宁技能详解
ao3入口镜像地址 ao3镜像入口可靠跳转
win11如何运行chkdsk命令 Win11检查和修复磁盘逻辑错误教程【修复】
win11怎么更改账户类型 Win11标准用户和管理员权限切换【教程】
哈尔滨城市通昵称修改方法
多闪电脑版下载_多闪PC端模拟器使用
composer 提示 "requires ext-soap" 缺少 SOAP 扩展怎么办?
J*aScript二进制处理_ArrayBuffer与Blob
C++ optional用法详解_C++17处理可能为空的返回值
Cassandra中复合主键、二级索引与ORDER BY排序的限制与解决方案
支付宝登录刷脸不是本人如何解决
Sublime怎么自动添加CSS前缀_Sublime安装Autoprefixer插件
Linux如何自动分析系统异常日志_Linux日志智能检测
Flask 应用中图片动态更新与上传:实现客户端定时刷新与服务器端文件管理
《幻兽帕鲁》手游帕鲁捕捉技巧分享
Excel如何制作月度销售统计图_Excel动态图表制作与控件应用
如何解决Casbin日志与应用日志不统一的问题,使用casbin/psr3-bridge实现无缝集成
AO3永久镜像入口开放_AO3最新网址兼容所有浏览器
《小宇宙》标记不友善评论方法
大熊猫抓取竹子的“大拇指”其实是什么?蚂蚁庄园课堂今天答案最新11月30日
DeepSeek超全面指南:入门必看
Go语言中方法与接收器:指针和值类型的调用机制详解
C++如何实现矩阵乘法_C++二维数组矩阵运算代码示例
暴风影音官网正式版_暴风影音手机版官网下载安卓
12306不能订票的时间段是固定的吗? | 节假日购票时间有无变化
抖音作品被限流怎么办 抖音内容优化与流量恢复方法
《绿竹漫游》关闭消息通知方法
CSS过渡与滚动滚动事件结合应用_scroll与transition动画
哔哩哔哩在线观看入口 B站官网免费进入
漫蛙manwa2网页版书签同步链接_漫蛙manwa多设备登录入口
精通VS Code多光标编辑以实现闪电般快速的修改
J*a中的值传递到底指什么_值传递模型在参数传递中的真正含义说明
Sublime怎么快速复制文件路径_Sublime右键菜单增强技巧
如何在CSS中使用absolute实现登录弹窗居中_transform translate结合
网易云音乐闹钟铃声设置教程
使用document.execCommand实现Web文本编辑器加粗/取消加粗
漫蛙官网(首页入口)_漫蛙漫画稳定访问教程分享
使用Selenium在无头Chrome中交互动态菜单和复选框的策略
高德地图导航路线偏差报警频繁怎么办 高德地图路线偏差修复与优化方法
MongoDB聚合管道:高效统计列表中各项的文档数量
《海贝音乐》均衡器设置方法
天天漫画2025最新入口 天天漫画永久有效登录入口
如何在mysql中设计餐饮点餐系统_mysql点餐系统项目实战
《东方财富》条件单关闭方法
《大周列国志》皇帝律令功能介绍
2025-11-09
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。