如何在Golang中实现并发消息队列消费者_Golang channel消息消费实践
技术百科
P粉602998670
发布时间:2026-01-01
浏览: 次 必须用 sync.WaitGroup 等待 worker 退出,因 for range 只感知 channel 关闭而不保证 goroutine 执行完毕;缓冲大小需权衡吞吐与内存,生产者单点 close,消费者只读 channel 保障安全。
用带缓冲 chan 做消费者队列最直接,但必须配 sync.WaitGroup 等待退出,否则主程序常提前结束——这是 90% 新手第一次跑不起来的根本原因。
为什么不能只用 for range 就完事?
看似简洁的 for data := range ch 确实能自动感知 close(ch) 并退出循环,但它只管“读完已关闭的 channel”,不管“goroutine 是否真正执行完毕”。一旦主 goroutine 执行完就退出进程,正在 sleep 或处理中的 worker 会被强制终止。
- 现象:
Worker 1 processing task 3: data-3打印一半,程序就静默退出 - 根本原因:没有同步机制告诉主程序“所有 worker 已退出”
- 正确做法:用
sync.WaitGroup显
式计数 + defer wg.Done(),不是靠 channel 关闭“猜”结束
缓冲大小设多少才不卡又不爆内存?
make(chan Task, N) 的 N 不是越大越好,它本质是生产者侧的“等待区”,和消费者吞吐能力强相关。
- 设太小(如
1):生产者频繁阻塞,尤其在突发任务时丢速明显 - 设太大(如
10000):内存占用陡增,且掩盖消费瓶颈——你以为是队列没满,其实是消费者卡在 DB 写入或 HTTP 调用上 - 经验值:从
100起步;若日志显示len(ch) == cap(ch)频繁出现,说明消费者跟不上,优先优化 worker 内部逻辑,而非盲目扩 buffer
多个消费者共用一个 chan 时,谁来关 channel?
只有一个角色能调用 close(ch):**生产者**。消费者绝不可 close,否则会 panic(panic: close of closed channel)。
- 错误模式:某个 worker 发现自己读到零值,就顺手
close(ch)—— 其他 worker 下一秒就崩溃 - 正确流程:生产者发完全部任务后,单点 close;所有消费者统一用
for task := range ch安全退出 - 进阶提醒:如果生产者是长连接(如监听 Kafka),则永不 close;此时需用
context.Context控制 worker 退出,而不是依赖 channel 关闭
package mainimport ( "fmt" "sync" "time" )
type Task struct { ID int Data string }
func worker(id int, tasks <-chan Task, wg sync.WaitGroup) { defer wg.Done() for task := range tasks { fmt.Printf("Worker %d processing task %d: %s\n", id, task.ID, task.Data) time.Sleep(300 time.Millisecond) // 模拟真实处理耗时 } fmt.Printf("Worker %d stopped.\n", id) }
func main() { taskQueue := make(chan Task, 100) var wg sync.WaitGroup
// 启动 3 个消费者 for i := 1; i <= 3; i++ { wg.Add(1) go worker(i, taskQueue, &wg) } // 生产者:发送 10 个任务 for i := 1; i <= 10; i++ { taskQueue <- Task{ID: i, Data: fmt.Sprintf("data-%d", i)} } close(taskQueue) // ✅ 只有这里能 close wg.Wait() // ✅ 必须等所有 worker 真正退出 fmt.Println("All workers done.")}
最易被忽略的点:worker 函数签名里接收的是
(只读 channel),这既是类型安全提示,也防止误写ch 导致编译失败——Go 的 channel 方向性不是装饰,是并发契约的一部分。
# ai # 的是 # 这是 # 多个 # 进阶 # 而不 # 单点 # http # go # golang # 循环 # 并发 # 主程序 # 内存占用 # 同步机制 # 为什么 # channel # 根本原因 # len # 太大 # for # cap # kafka # 经验值
相关栏目: <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 AI推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 SEO优化<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 技术百科<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 谷歌推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 百度推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 网络营销<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 案例网站<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 精选文章<?muma echo $count; ?> 】
相关推荐
- 如何在Golang中指定模块版本_使用go.mod
- php打包exe怎么传递参数_命令行参数接收方法【
- Win11怎样激活系统密钥_Win11系统密钥激活
- 如何使用Golang实现云原生应用弹性伸缩_自动应
- Windows10如何更改桌面图标间距_Win10
- Win11屏幕亮度突然变暗怎么解决_自动变暗问题处
- LINUX的SELinux是什么_详解LINUX强
- Go 语言标准库为何不提供泛型 Contains
- 如何提升Golang JSON序列化性能_Gola
- Win11怎么清理C盘OneDrive缓存_Win
- 如何使用Golang实现容器安全扫描_Golang
- Drupal 中渲染节点时出现 HTML 标签嵌套
- Linux怎么查找死循环进程_Linux系统负载分
- Windows如何拦截2345弹窗广告_Windo
- php8.4如何实现队列任务_php8.4redi
- Windows7如何安装系统镜像_Windows7
- Flask 表单数据通过 SMTP 发送邮件的完整
- Win11怎么关闭透明效果_Windows11辅助
- Mac怎么开启“任何来源”_Mac安装未签名应用的
- php怎么下载安装后无法解析php文件_服务器配置
- php高频调试功能有哪些_php常用调试函数与工具
- Windows10怎样连接蓝牙设备_Windows
- Win11怎么设置任务栏对齐方式_Windows1
- php会话怎么开启_session_start函数
- Win11怎么更改文件夹图标_自定义Win11文件
- C++如何解析JSON数据?(nlohmann/j
- Python 中将 ISO 8601 时间戳转换为
- Windows10电脑怎么设置电源按钮_Win10
- c++输入输出流 c++ cin与cout格式化输
- PHP的Workerman对架构扩展有啥帮助_应用
- Windows笔记本无法进入睡眠模式怎么办?(电源
- Win11更新后变慢怎么办_Win11系统更新后卡
- Mac如何彻底清理浏览器缓存?(Safari与Ch
- LINUX怎么查看进程_LINUX ps命令查看运
- Mac的“调度中心”与“空间”怎么用_Mac多桌面
- Windows 11如何开启文件夹加密(EFS)_
- PHP主流架构如何做单元测试_工具与流程【详解】
- 微信里的php文件怎么变mp4_微信接收php转m
- Win11怎么设置右键刷新选项_Windows11
- Win10怎样安装Word样式库_Win10安装W
- 如何在包含多值的列中精准搜索指定演员?
- Windows10如何更改盘符名称_Win10重命
- 如何使用Golang实现基本类型比较_Golang
- Python lxml的etree和Element
- Windows 10怎么把任务栏放在屏幕上方_Wi
- Win11怎么设置默认图片查看器_Windows1
- PythonGIL机制理解_多线程限制解析【教程】
- 如何在Windows上设置闹钟和计时器_系统自带的
- 如何使用Golang实现聊天室消息存档_存储聊天记
- php485返回空数组怎么回事_php485数据接

式计数 +
QQ客服