0

0

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

霞舞

霞舞

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

|

126人浏览过

|

来源于php中文网

原创

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

当对未关闭的无缓冲通道使用 `for range` 时,循环会在所有数据读取完毕后持续阻塞等待新值,而发送方 goroutine 已退出且通道未关闭,最终引发“all goroutines are asleep”死锁。

在 Go 中,for v := range ch 语句具有明确的语义:它会持续接收通道 ch 中的值,直到该通道被显式关闭(close(ch))。一旦通道关闭,range 自动退出循环;若通道始终保持打开状态,即使所有发送 Goroutine 已完成并退出,range 仍会无限阻塞——因为 Go 运行时无法自动推断“不会再有新值发送”,它只依赖 close 信号作为终止依据。

你的原始代码中:

  • 创建了无缓冲通道 my_channel := make(chan int)
  • 启动 3 个 Goroutine 调用 sum_up(i, my_channel),分别向通道发送 1、3、6
  • 主 Goroutine 执行 for ele := range my_channel —— 成功接收并打印全部 3 个值
  • 但此时通道仍处于打开且空闲状态,range 继续等待下一个值
  • 所有 sum_up Goroutine 已执行完毕并退出,主 Goroutine 又未关闭通道,也无其他 Goroutine 可写入
  • 最终所有 Goroutine(仅剩主 Goroutine 在阻塞等待)陷入休眠 → 触发 fatal deadlock

✅ 正确做法是:在确认所有发送操作完成后,由某个 Goroutine 显式关闭通道。常用模式是结合 sync.WaitGroup 等待所有发送者结束,再关闭通道:

YouMind
YouMind

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)

    // 启动发送 Goroutine,并注册 WaitGroup 计数
    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 可以退出
    }()

    // range 安全退出:收到所有值 + 通道关闭信号
    for ele := range my_channel {
        fmt.Println(ele)
    }

    fmt.Println("Done")
}

⚠️ 注意事项:

  • 切勿在发送 Goroutine 中直接 close(ch):多个 Goroutine 同时关闭同一通道会 panic(close of closed channel)
  • WaitGroup 必须在启动 Goroutine 前调用 Add(),且 Done() 应确保执行(推荐 defer)
  • 若使用带缓冲通道(如 make(chan int, 3)),问题本质不变——range 仍需 close 才能退出
  • 替代方案:使用 select + done 通道实现超时或手动控制,但 close + range 是最符合 Go 惯用法的简洁解法

总结:Go 的 for range 与通道生命周期强绑定,关闭通道是唯一合法的终止信号。理解这一设计原则,是写出健壮并发程序的关键基础。

相关专题

更多
string转int
string转int

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

315

2023.08.02

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

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

537

2024.08.29

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

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

52

2025.08.29

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

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

194

2025.08.29

Golang channel原理
Golang channel原理

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

244

2025.11.14

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

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

342

2025.11.17

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

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

11

2026.01.13

PHP 高性能
PHP 高性能

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

21

2026.01.13

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

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

18

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号