0

0

Go 中使用 for range 遍历通道时的死锁问题及正确关闭通道的方法

心靈之曲

心靈之曲

发布时间:2026-01-14 10:08:02

|

889人浏览过

|

来源于php中文网

原创

Go 中使用 for range 遍历通道时的死锁问题及正确关闭通道的方法

当对未关闭的无缓冲通道使用 `for range` 时,循环会在所有数据读取完毕后持续阻塞等待新值,而若发送方已全部退出且未关闭通道,程序将陷入死锁。

在 Go 中,for ele := range ch 是一种简洁安全的通道遍历语法,但它有一个关键前提:该通道必须被显式关闭(close(ch)),否则循环永远不会结束——它会一直阻塞,等待下一个值到来,即使所有 goroutine 已完成发送。

你的代码中存在典型的“未关闭通道导致死锁”问题:

my_channel := make(chan int) // 无缓冲通道
for i := 2; i < 5; i++ {
    go sum_up(i, my_channel) // 启动 3 个 goroutine,各发 1 个值
}
for ele := range my_channel { // ✅ 成功读取 1、3、6
    fmt.Println(ele)
} // ❌ 此处卡住:无更多数据,且通道未关闭 → 死锁

虽然三个 sum_up goroutine 已成功向通道发送了 0+1=1、0+1+2=3、0+1+2+3=6 并退出,但主 goroutine 的 range 循环并不知道“发送已全部完成”。Go 的通道模型中,只有关闭通道才能向 range 发出“不会再有新值”的信号;单纯等待发送方退出是不够的——因为无法保证它们是否真的已完成(可能存在竞态或延迟)。

✅ 正确做法:协作式关闭通道

需引入同步机制(如 sync.WaitGroup),让主 goroutine 确认所有发送者完成后再关闭通道:

来福FM
来福FM

来福 - 你的私人AI电台

下载
package main

import (
    "fmt"
    "sync"
)

func sum_up(my_int int, cs chan int, wg *sync.WaitGroup) {
    defer wg.Done() // 确保无论何种路径都调用 Done()
    my_sum := 0
    for i := 0; i < my_int; i++ {
        my_sum += i
    }
    cs <- my_sum
}

func main() {
    wg := &sync.WaitGroup{}
    my_channel := make(chan int)

    // 启动 goroutine 并注册等待
    for i := 2; i < 5; i++ {
        wg.Add(1)
        go sum_up(i, my_channel, wg)
    }

    // 单独 goroutine 等待全部完成并关闭通道
    go func() {
        wg.Wait()
        close(my_channel) // ? 关键:通知 range 循环终止
    }()

    // 安全遍历:收到所有值后自动退出
    for ele := range my_channel {
        fmt.Println(ele)
    }

    fmt.Println("Done")
}
? 注意:close() 必须由发送方或协调者调用,且只能调用一次;对已关闭通道再次 close 会 panic。此处由独立 goroutine 调用,避免主 goroutine 在 range 前就关闭(导致漏读)或在 range 后才关闭(导致死锁)。

? 替代方案(更现代推荐)

若使用 Go 1.21+,可考虑 errgroup.Group 或结合 context 实现超时控制;但对于本例,WaitGroup + close 是最清晰、零依赖的标准解法。

✅ 总结

  • for range ch 会阻塞直到通道被关闭
  • 发送 goroutine 退出 ≠ 通道可安全关闭,需显式同步;
  • 使用 sync.WaitGroup 是协调多 goroutine 发送后关闭通道的惯用模式;
  • 切勿在发送未完成前关闭通道,也切勿遗漏关闭——二者均会导致逻辑错误或死锁。

遵循这一模式,即可写出健壮、无死锁的并发通道处理代码。

相关专题

更多
Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

37

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

37

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

16

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

jQuery 正则表达式相关教程
jQuery 正则表达式相关教程

本专题整合了jQuery正则表达式相关教程大全,阅读专题下面的文章了解更多详细内容。

3

2026.01.13

交互式图表和动态图表教程汇总
交互式图表和动态图表教程汇总

本专题整合了交互式图表和动态图表的相关内容,阅读专题下面的文章了解更多详细内容。

45

2026.01.13

nginx配置文件详细教程
nginx配置文件详细教程

本专题整合了nginx配置文件相关教程详细汇总,阅读专题下面的文章了解更多详细内容。

9

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3.7万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号