当前位置: 首页
编程语言
如何安全关闭多个 goroutine 共用的 Go 通道

如何安全关闭多个 goroutine 共用的 Go 通道

热心网友 时间:2026-04-30
转载

如何安全关闭多个 goroutine 共用的 Go 通道

在 Go 的并发世界里,通道(channel)是协程间通信的基石,好用但“脾气”不小。它有一条铁律:一个通道只能被关闭一次,而且关闭之后,任何发送操作都会立刻引发 panic。这就像一扇门,只能由一个人来上锁,锁上之后谁也别想再往里推东西,否则门框都得晃三晃。

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈

文章开头那段代码的症结就在于此:10个并发的 `gen` 协程,每个都在干完活后试图去关同一扇门。结果是,手最快的那个协程把门关上了,后面姗姗来迟的几位却还想着往里塞数据,程序不崩溃才怪。

如何安全关闭多个 goroutine 共用的 Go 通道

✅ 正确解法:WaitGroup + 单点关闭

那么,正确的姿势是什么?核心原则就两条:关闭操作必须等到所有“发送者”都确认退场之后才能进行,并且这个动作只能发生一次

实现这个目标,Go 标准库里的 `sync.WaitGroup` 是绝佳搭档。它的工作模式很清晰:

  • 每启动一个发送协程前,用 `wg.Add(1)` 登记一下,告诉 WaitGroup:“又多了一个人等会儿需要你关照”。
  • 在每个发送协程的内部,用 `defer wg.Done()` 确保无论这个协程是正常结束还是中途“翻车”(panic),都会在退出时举手报告:“我这边完事了”。
  • 然后,我们启动一个独立的、专门负责关门的协程。它啥也不干,就调用 `wg.Wait()` 安静地等着,直到所有登记在册的发送协程都报告“Done”了,它才从容地执行 `close(ch)`。
  • 接收端保持不变,继续用 `for i := range ch` 这种简洁的语法,它能自动在通道关闭且数据被取空后优雅地结束循环。

下面就是按照这个思路修正后的、可以直接运行的完整代码:

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)
}

⚠️ 关键注意事项

方案虽好,但魔鬼藏在细节里。实施时,有几个坑点需要特别留意:

  • 关门这事,最好别让主协程干:如果把 `wg.Wait()` 和 `close(ch)` 直接放在 `main` 函数里顺序执行,主协程在关闭通道后会立刻退出。如果接收协程(比如 `receiver`)不是在主协程中同步运行的,就可能被强行终止,导致部分数据“胎死腹中”。好在上面代码里,`receiver(ch)` 是同步调用的,所以能稳稳地收完所有数据。
  • `defer wg.Done()` 是道保险:这是一种防御性编程。即使 `gen` 函数内部发生了意想不到的 panic,`defer` 语句也能保证 `Done()` 被调用,从而避免 `wg.Wait()` 永远等不到人,造成程序死锁。
  • 缓冲通道照用不误:这个方案对带缓冲的通道(`make(chan int, N)`)同样有效,逻辑完全一样,无需任何调整。
  • 边界条件要抠细:回头看看原示例里的 `if i > 100`,这个条件会导致循环在 `i` 变成 101 时才跳出,但此时 `i=100` 已经被发送出去了。这意味着实际会生成 0 到 100 共 101 个数。通常我们的意图是发送 100 个,所以建议改为 `if i >= 100` 或者 `if i == 100`。

✅ 总结

处理多生产者通道的关闭问题,可以总结为一条黄金法则:创建者负责协调,用 WaitGroup 清点人数,用独立协程执行关闭。这不仅仅是一个避免 panic 的技术技巧,更是 Go 并发哲学“责任明确”和“生命周期解耦”的生动体现。熟练掌握这个模式,无论是数据库的分页读取、多个事件流的聚合,还是分布式任务的分发与收集,你都能处理得游刃有余。

来源:https://www.php.cn/faq/2393175.html
上一篇: golang语言怎么学

游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。

同类文章
更多
使用Python合并与拆分Excel单元格的实用方法

使用Python合并与拆分Excel单元格的实用方法

使用Python合并与拆分Excel单元格的实用方法 处理Excel表格时,合并单元格是个绕不开的操作。无论是为了制作清晰美观的表头,还是为了突出显示某些关键信息,这个功能都相当实用。不过,当需要批量处理或者将流程自动化时,手动在Excel里点点划划就有点力不从心了。今天,我们就来聊聊如何用Pyth

时间:2026-04-30 12:32
SpringBoot OpenFeign整合okHttpClient实践

SpringBoot OpenFeign整合okHttpClient实践

前言 在SpringCloud微服务架构中,服务间的数据传输,OpenFeign无疑是那个既简单又好用的选择。不过,它默认使用的客户端是JDK自带的HttpURLConnection,这里有个小细节值得注意:这个客户端本身并不具备连接池功能。 这意味着什么?简单来说,每一次发起远程调用,系统都会尝试

时间:2026-04-30 12:32
修改JAR文件并重新打包的两种方式

修改JAR文件并重新打包的两种方式

本文介绍两种修改 JAR 包内文件(如配置文件或 Class 文件)后重新打包的方式:Ja va 命令方式 与 Ant 脚本方式。 核心警告 对于 Spring Boot 的可执行 JAR 包,重新打包时严禁使用压缩(必须使用存储模式),否则会导致 ClassNotFoundException 或启

时间:2026-04-30 12:31
C++中INI配置文件读取技术详解

C++中INI配置文件读取技术详解

一、INI文件格式概述 在众多配置文件格式中,INI(Initialization)格式堪称经典。它以纯文本形式存储,结构清晰直观,既便于开发者手动编辑与维护,也易于程序进行自动化解析与读取。这种简单高效的特点,使其在软件配置、游戏设置、系统参数管理等场景中,至今仍被广泛应用。 1 1 基本结构 一

时间:2026-04-30 12:31
idea如何保存当前已修改的文件|恢复到未修改状态

idea如何保存当前已修改的文件|恢复到未修改状态

1、打开git,如下步骤1 先来看第一张图,这是整个操作的起点。 在步骤2的区域,你会看到所有被修改过的文件都列在这里,一目了然。 而步骤3指向的代码区域,正是我们修改后还在报错的部分,问题就出在这儿。 这里有个关键细节:注意看圈4标识的地方,你所有修改过的代码行,IDE都会用淡绿色的背景高亮显示,

时间:2026-04-30 12:31
热门专题
更多
刀塔传奇破解版无限钻石下载大全 刀塔传奇破解版无限钻石下载大全
洛克王国正式正版手游下载安装大全 洛克王国正式正版手游下载安装大全
思美人手游下载专区 思美人手游下载专区
好玩的阿拉德之怒游戏下载合集 好玩的阿拉德之怒游戏下载合集
不思议迷宫手游下载合集 不思议迷宫手游下载合集
百宝袋汉化组游戏最新合集 百宝袋汉化组游戏最新合集
jsk游戏合集30款游戏大全 jsk游戏合集30款游戏大全
宾果消消消原版下载大全 宾果消消消原版下载大全
  • 日榜
  • 周榜
  • 月榜
热门教程
更多
  • 游戏攻略
  • 安卓教程
  • 苹果教程
  • 电脑教程