0

0

如何正确使用 for-range 遍历 Go 中的无缓冲通道以避免死锁

花韻仙語

花韻仙語

发布时间:2026-01-14 18:26:01

|

681人浏览过

|

来源于php中文网

原创

如何正确使用 for-range 遍历 Go 中的无缓冲通道以避免死锁

go 中对未关闭的无缓冲通道执行 `for range` 会永久阻塞,导致所有 goroutine 休眠而触发死锁;必须在所有发送者完成写入后显式关闭通道,才能让 range 循环正常退出。

在 Go 并发编程中,for ele := range ch 是遍历通道的惯用写法,但它有一个关键前提:该通道必须被关闭(closed),否则循环将永远等待下一个值——即使所有生产者 goroutine 已执行完毕并退出。

你遇到的死锁正是源于此:程序启动了 3 个 sum_up goroutine 向 my_channel 发送数据(对应 i=2,3,4,结果分别为 1, 3, 6),主 goroutine 通过 for range 成功读取这 3 个值并打印。但此时通道仍处于打开状态,range 循环继续尝试接收第 4 个值,而所有 sum_up goroutine 已结束、无其他 goroutine 向通道写入,也无人关闭通道——于是主 goroutine 阻塞,其余 goroutine 全部退出,最终触发 fatal error: all goroutines are asleep - deadlock!。

✅ 正确解法是:确保通道在所有发送操作完成后被关闭。由于发送操作在多个 goroutine 中异步进行,不能由主 goroutine 直接判断何时关闭,因此需借助同步原语协调。常用且推荐的方式是 sync.WaitGroup:

Removal.AI
Removal.AI

AI移出图片背景工具

下载
package main

import (
    "fmt"
    "sync"
)

func sum_up(my_int int, cs chan int, wg *sync.WaitGroup) {
    defer wg.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)

    // 启动 3 个并发任务
    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 循环退出
    }()

    // 主 goroutine 安全遍历(自动在 close 后终止)
    for ele := range my_channel {
        fmt.Println(ele)
    }

    fmt.Println("Done")
}

? 关键要点总结:

  • for range ch 仅在通道 关闭后自动退出,它不会因“暂无数据”而停止,也不会感知发送者是否存活;
  • 关闭通道的责任应由协调者(通常是启动 goroutine 的一方)承担,而非发送者——因为单个发送者无法知道其他发送者是否已完成;
  • 使用 defer wg.Done() 可提升代码健壮性,避免 panic 时漏掉计数;
  • 切勿在多个 goroutine 中重复调用 close(ch),会导致 panic;确保仅关闭一次(本例中由 wg.Wait() 后的唯一 goroutine 执行);
  • 若需带超时或更复杂控制,可结合 select + time.After 或 context,但本场景 WaitGroup + close 是最简洁、标准的解决方案。

遵循这一模式,即可安全、清晰地实现多生产者 → 单消费者通道遍历,彻底规避此类死锁问题。

相关专题

更多
scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

187

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

271

2023.10.25

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

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

34

2026.01.14

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

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

14

2026.01.13

PHP 高性能
PHP 高性能

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

33

2026.01.13

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

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

18

2026.01.13

PHP 文件上传
PHP 文件上传

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

12

2026.01.13

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

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

6

2026.01.13

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

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

3

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号