您的位置:首页 >如何安全关闭多个 goroutine 共用的 Go 通道
发布于2026-04-30 阅读(0)
扫一扫,手机访问
在 Go 的并发世界里,通道(channel)是协程间通信的基石,好用但“脾气”不小。它有一条铁律:一个通道只能被关闭一次,而且关闭之后,任何发送操作都会立刻引发 panic。这就像一扇门,只能由一个人来上锁,锁上之后谁也别想再往里推东西,否则门框都得晃三晃。
文章开头那段代码的症结就在于此:10个并发的 `gen` 协程,每个都在干完活后试图去关同一扇门。结果是,手最快的那个协程把门关上了,后面姗姗来迟的几位却还想着往里塞数据,程序不崩溃才怪。

那么,正确的姿势是什么?核心原则就两条:关闭操作必须等到所有“发送者”都确认退场之后才能进行,并且这个动作只能发生一次。
实现这个目标,Go 标准库里的 `sync.WaitGroup` 是绝佳搭档。它的工作模式很清晰:
下面就是按照这个思路修正后的、可以直接运行的完整代码:
package main
import (
"fmt"
"sync"
"time"
)
func gen(ch chan int, wg *sync.WaitGroup) {
defer wg.Done() // 确保 goroutine 结束时登记完成
for i := 0; ; i++ {
time.Sleep(time.Millisecond * 10)
select {
case ch <- i:
// 发送成功
default:
// 可选:非阻塞发送失败时优雅退出(如接收端已提前关闭)
return
}
if i >= 100 { // 注意:i > 100 会导致多发一次,应为 >= 100 或 i == 100
break
}
}
}
func receiver(ch chan int) {
for i := range ch {
fmt.Println("received:", i)
}
}
func main() {
ch := make(chan int)
var wg sync.WaitGroup
// 启动 10 个发送 goroutine
for i := 0; i < 10; i++ {
wg.Add(1)
go gen(ch, &wg)
}
// 在新 goroutine 中等待全部发送者完成,然后关闭通道
go func() {
wg.Wait()
close(ch)
}()
// 主 goroutine 执行接收逻辑(阻塞直到通道关闭)
receiver(ch)
}
方案虽好,但魔鬼藏在细节里。实施时,有几个坑点需要特别留意:
处理多生产者通道的关闭问题,可以总结为一条黄金法则:创建者负责协调,用 WaitGroup 清点人数,用独立协程执行关闭。这不仅仅是一个避免 panic 的技术技巧,更是 Go 并发哲学“责任明确”和“生命周期解耦”的生动体现。熟练掌握这个模式,无论是数据库的分页读取、多个事件流的聚合,还是分布式任务的分发与收集,你都能处理得游刃有余。
上一篇:golang语言怎么学
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
9