0

0

Go Channels: 如何让无缓冲通道发送操作不阻塞?

心靈之曲

心靈之曲

发布时间:2026-02-12 12:31:13

|

440人浏览过

|

来源于php中文网

原创

Go Channels: 如何让无缓冲通道发送操作不阻塞?

本文详解 go 中无缓冲通道的阻塞机制,指出 `chin

在 Go 并发编程中,通道(channel)是 Goroutine 间通信的核心原语,但其行为常被误解——尤其是“非阻塞”这一概念。你遇到的问题非常典型:程序在 chin <- vin 处卡死,即使已启动 5 个 worker(),仍无法完成数据发送循环。根本原因在于:无缓冲通道(make(chan T))的发送操作天生就是同步且阻塞的——它必须等待至少一个 Goroutine 同时执行对应的接收操作(<-chin),二者“手递手”完成数据传递后才继续执行。

你观察到“阻塞在第 5 次发送后”,恰好印证了这一点:5 个 worker() 启动后立即进入 for res := range chin,开始等待接收;但若 rows.Next() 循环在 worker() 真正开始接收前就发起第一次发送,而此时尚无接收方就绪,发送即阻塞。更关键的是,你的主 goroutine 在发送完所有数据后,才去读 chout,而 worker() 的 chout <- v 同样是无缓冲的——当所有 worker 都在尝试发送结果但主 goroutine 还未开始接收时,chout 也会阻塞,形成死锁闭环。

✅ 正确解法:三步破局

1. 使用带缓冲的通道(最直接)

为 chin 和 chout 设置合理容量,使发送端无需等待接收即可暂存数据:

var chin = make(chan in, 100)   // &#32531;&#20914;&#21306;&#23481;&#32435; 100 &#20010;&#24453;&#22788;&#29702;&#20219;&#21153;
var chout = make(chan out, 100) // &#32531;&#20914;&#21306;&#23481;&#32435; 100 &#20010;&#22788;&#29702;&#32467;&#26524;

这样 chin <- vin 最多在缓冲区满时阻塞(如你实验中“阻塞在第 105 次”),而非立即阻塞。但需注意:缓冲大小应基于内存与吞吐权衡,过大易 OOM,过小仍可能阻塞。

CodeWP
CodeWP

针对 WordPress 训练的AI代码生成器

下载

2. 解耦发送与接收逻辑(推荐实践)

将 chout 的消费逻辑移至独立 goroutine,避免主流程串行依赖:

// &#21551;&#21160;&#21518;&#21488;&#28040;&#36153;&#32773;&#65292;&#25345;&#32493;&#22788;&#29702;&#32467;&#26524;
go func() {
    for res := range chout {
        update5.Exec(res.data, res.id)
    }
}()

// &#20027; goroutine &#19987;&#27880;&#21457;&#36865;&#20219;&#21153;
for rows.Next() {
    // ... &#25195;&#25551;&#25968;&#25454;
    chin <- vin // &#27492;&#22788;&#20173;&#21487;&#33021;&#22240; chin &#26080;&#32531;&#20914;&#32780;&#38459;&#22622;&#65292;&#20294; chout &#24050;&#35299;&#32806;
    numtodo++
}
rows.Close()

// &#21457;&#36865;&#23436;&#27605;&#21518;&#20851;&#38381;&#36755;&#20837;&#36890;&#36947;&#65292;&#36890;&#30693; workers &#36864;&#20986;
close(chin)

3. 显式关闭通道 + 使用 range(健壮收尾)

仅靠 for range 读取通道是不够的——若通道永不关闭,range 将永远阻塞。务必在所有发送完成后调用 close():

// &#25152;&#26377;&#20219;&#21153;&#21457;&#36865;&#23436;&#27605;&#21518;
close(chin) // &#21578;&#35785;&#25152;&#26377; worker&#65306;&#19981;&#20877;&#26377;&#26032;&#20219;&#21153;

// worker &#20869;&#37096;&#33258;&#21160;&#36864;&#20986;
func worker() {
    for res := range chin { // &#24403; chin &#20851;&#38381;&#26102;&#65292;&#24490;&#29615;&#33258;&#28982;&#32467;&#26463;
        var v out
        v.id = res.id
        v.data = process(res.data)
        chout <- v
    }
}

// &#21516;&#29702;&#65292;&#22788;&#29702;&#23436;&#25152;&#26377;&#32467;&#26524;&#21518;&#20851;&#38381;&#36755;&#20986;&#36890;&#36947;
go func() {
    for res := range chout {
        update5.Exec(res.data, res.id)
    }
    close(chout) // &#21487;&#36873;&#65292;&#33509;&#21518;&#32493;&#26080;&#20854;&#20182;&#25509;&#25910;&#32773;&#21017;&#38750;&#24517;&#38656;
}()

⚠️ 关键注意事项

  • 永远不要对 nil 通道或已关闭的通道执行发送操作,这会引发 panic;
  • close(ch) 只能由发送方调用,且只能调用一次;
  • 若使用 select 实现非阻塞发送,需配合 default 分支(但本场景中缓冲+关闭已足够);
  • 数据库操作(如 rows.Scan)本身可能阻塞,确保 nextbatch2.Query() 返回的 rows 可正常迭代,避免底层连接问题被误判为通道阻塞。

通过以上改造,你的管道模型将变为:主 goroutine 快速注入任务 → 多 worker 并行处理 → 后台 goroutine 异步消费结果,彻底消除发送阻塞,实现真正高效的并发流水线。

热门AI工具

更多
DeepSeek
DeepSeek

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

豆包大模型
豆包大模型

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

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

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

文心一言
文心一言

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

讯飞写作
讯飞写作

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

即梦AI
即梦AI

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

ChatGPT
ChatGPT

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

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

206

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

235

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

346

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

212

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

402

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

322

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

197

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

784

2025.06.17

2026春节习俗大全
2026春节习俗大全

本专题整合了2026春节习俗大全,阅读专题下面的文章了解更多详细内容。

189

2026.02.11

热门下载

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

精品课程

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

共32课时 | 5万人学习

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号