0

0

Go 中使用 for range 遍历未关闭的通道导致死锁的原理与解决方案

花韻仙語

花韻仙語

发布时间:2026-01-14 11:03:15

|

506人浏览过

|

来源于php中文网

原创

Go 中使用 for range 遍历未关闭的通道导致死锁的原理与解决方案

当对未关闭的无缓冲通道使用 `for range` 时,循环会在所有已发送值被读取后持续阻塞,等待更多数据或通道关闭;若无 goroutine 再向通道写入且通道未关闭,程序将因所有 goroutine 休眠而触发死锁。

在 Go 中,for ele := range ch 是一种简洁遍历通道的方式,但它隐含一个重要前提:该通道最终必须被关闭。否则,一旦所有已发送的数据被消费完毕,range 循环会无限期阻塞,等待下一次接收——而此时若所有生产者 goroutine 已退出、且无人负责关闭通道,主 goroutine 就会永远卡住,运行时检测到“所有 goroutine 处于休眠状态”,从而 panic 报出 fatal error: all goroutines are asleep - deadlock!。

你的代码中,三个 sum_up goroutine 分别向 my_channel 发送 1、3、6(对应 i=2,3,4 的前 i 个自然数之和),并成功完成退出。但主 goroutine 的 for range 在读完这三个值后,并未终止,而是继续尝试从通道接收——而此时已无任何 goroutine 向该通道发送新值,且通道未被关闭,因此陷入永久阻塞。

关键点在于:range 不会因“发送端退出”自动结束,只响应“通道关闭”事件。即使所有写入 goroutine 执行完毕,只要通道未显式关闭,range 就不会退出。

✅ 正确做法是:在确认所有生产者完成写入后,由某个 goroutine(通常是协调者)调用 close(ch)。常见模式是结合 sync.WaitGroup 追踪活跃的生产者:

云从科技AI开放平台
云从科技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() {
    var wg sync.WaitGroup
    my_channel := make(chan int)

    // 启动 3 个生产者 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 循环可以安全退出
    }()

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

    fmt.Println("Done")
}

⚠️ 注意事项:

  • 不要在多个 goroutine 中重复调用 close(),会导致 panic;
  • close() 只能用于 发送端已明确不再写入 的场景(通常由生产者方或协调者调用);
  • 若使用带缓冲通道(如 make(chan int, 3)),仍需关闭才能让 range 正常退出;
  • 替代方案包括:使用 select + done channel 实现超时或手动控制;或改用 for i := 0; i < N; i++ { v := <-ch }(需预先知道接收数量)。

总结:Go 的通道设计强调显式同步语义。for range 是便利语法糖,但绝非“自动感知生产者生命周期”的智能机制。理解“关闭通道 = 终止 range”这一契约,是写出健壮并发程序的基础。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

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

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

492

2023.10.18

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

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

382

2023.10.25

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

1031

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

612

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

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

334

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

235

2025.08.29

Golang channel原理
Golang channel原理

本专题整合了Golang channel通信相关介绍,阅读专题下面的文章了解更多详细内容。

261

2025.11.14

golang channel相关教程
golang channel相关教程

本专题整合了golang处理channel相关教程,阅读专题下面的文章了解更多详细内容。

351

2025.11.17

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

37

2026.03.12

热门下载

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

精品课程

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

共32课时 | 6.1万人学习

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

共10课时 | 0.9万人学习

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

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