尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Channel详解:无缓冲vs有缓冲的选择
Channel详解无缓冲vs有缓冲的选择摘要Go channel深入解析包括无缓冲channel同步机制、有缓冲channel异步通信、channel关闭与range遍历、单向channel方向限制、nil channel的妙用、常见死锁场景排查。上篇聊完goroutine有个问题就来了goroutine之间怎么传数据。我刚学Go的时候直接用全局变量加mutex写完被同事吐槽你这是把Java那套硬搬过来了。Go的并发哲学是通过通信来共享内存channel就是干这个的。我第一次用channel的时候踩了个死锁的坑程序跑起来直接hang住排查了两个小时才搞明白无缓冲channel的发送和接收必须同时就绪。这篇我把channel的用法和原理讲透尤其是无缓冲和有缓冲的选择很多人搞混。一、无缓冲Channel无缓冲channel就是make的时候不传第二个参数。它的特点是发送和接收必须同时就绪发送方会阻塞直到有人接收反之亦然。本质上是同步通信。packagemainimportfmtfuncmain(){// 创建无缓冲channel传递int类型数据// make(chan int) 第二个参数为0或不传就是无缓冲ch:make(chanint)// 启动一个goroutine接收数据gofunc(){// 这里会阻塞直到main goroutine发送数据val:-ch fmt.Printf(收到数据: %d\n,val)}()// 发送数据这里也会阻塞直到goroutine准备好接收// 无缓冲channel的发送和接收是同步的类似面对面交接ch-42fmt.Println(发送完成)}无缓冲channel最典型的用途是做信号通知和同步等待。比如等一个goroutine完成初始化后再继续。packagemainimportfmtfuncmain(){// 用无缓冲channel做完成信号done:make(chanstruct{})gofunc(){// 执行一些初始化工作fmt.Println(初始化中...)// 初始化完成发送信号// struct{}不占内存适合做纯信号close(done)}()// 阻塞等待初始化完成// close后的channel接收操作立即返回零值-done fmt.Println(初始化完成继续执行)}二、有缓冲Channel有缓冲channel就是make的时候传第二个参数指定缓冲区大小。发送方在缓冲区没满之前不阻塞接收方在缓冲区没空之前不阻塞。本质上是异步通信。packagemainimportfmtfuncmain(){// 创建缓冲大小为3的channel// 可以缓存3个int值发送方在前3次发送时不会阻塞ch:make(chanint,3)// 连续发送3个数据不会阻塞// 因为缓冲区大小是3刚好放满ch-1ch-2ch-3// 打印当前缓冲区中的数据量fmt.Printf(缓冲区数据量: %d\n,len(ch))// 输出3fmt.Printf(缓冲区容量: %d\n,cap(ch))// 输出3// 接收数据按先进先出的顺序fmt.Println(-ch)// 输出1fmt.Println(-ch)// 输出2fmt.Println(-ch)// 输出3}有缓冲channel适合做生产者-消费者模型。生产者生产数据放进channel消费者从channel取数据处理两边各跑各的。packagemainimport(fmtsynctime)funcmain(){varwg sync.WaitGroup// 创建缓冲大小为10的channel// 相当于在生产者和消费者之间放了一个队列ch:make(chanint,10)// 生产者goroutinewg.Add(1)gofunc(){deferwg.Done()fori:0;i20;i{// 生产数据发送到channel// 缓冲区满了会阻塞等消费者取走数据ch-i fmt.Printf(生产: %d\n,i)time.Sleep(50*time.Millisecond)}// 生产完毕关闭channel// 关闭后消费者还能继续读取剩余数据close(ch)}()// 消费者goroutinewg.Add(1)gofunc(){deferwg.Done()// range遍历channel自动在channel关闭后退出forval:rangech{fmt.Printf( 消费: %d\n,val)time.Sleep(100*time.Millisecond)}fmt.Println(消费完毕)}()wg.Wait()}三、Channel的关闭和遍历channel可以关闭关闭后不能再发送数据但可以继续接收。遍历channel用rangechannel关闭且缓冲区清空后自动退出循环。packagemainimportfmtfuncmain(){ch:make(chanstring,3)// 发送数据ch-helloch-worldch-go// 关闭channel// 关闭后不能再发送否则会panic// 但可以继续接收缓冲区里的数据close(ch)// 方式一用range遍历自动退出formsg:rangech{fmt.Println(msg)}// 方式二用comma ok模式判断是否关闭ch2:make(chanint,2)ch2-1ch2-2close(ch2)// ok为false表示channel已关闭且缓冲区已空for{val,ok:-ch2if!ok{fmt.Println(channel已关闭)break}fmt.Printf(收到: %d\n,val)}}有个规则要记牢channel只能由发送方关闭接收方不要关。向已关闭的channel发送数据会panic。四、单向ChannelGo支持单向channel用于限制channel的方向。函数参数用单向channel可以在编译期防止误操作。packagemainimportfmt// 只能发送的channel类型chan- 表示只能写入funcproducer(outchan-int){fori:0;i5;i{out-i*10// out - i * 10 可以发送数据// val : -out 会编译报错不能接收}close(out)// 发送方负责关闭}// 只能接收的channel类型-chan 表示只能读取funcconsumer(in-chanint){// in - 100 会编译报错不能发送forval:rangein{fmt.Printf(消费数据: %d\n,val)}}funcmain(){// 创建双向channelch:make(chanint,5)// 双向channel可以隐式转换为单向// 编译器自动把chan int转成chan- int或-chan intgoproducer(ch)consumer(ch)}独家踩坑向已关闭的Channel发送数据有一次线上服务突然panic日志里全是send on closed channel。排查发现是一个数据处理管道有多个goroutine往同一个channel写数据某个goroutine处理完后调了close。其他goroutine还在往里写直接panic。代码大概是这样的。// 错误代码多个goroutine写同一个channel其中一个close了funcprocessData(inputs[]int){ch:make(chanint,10)for_,input:rangeinputs{gofunc(valint){result:heavyCompute(val)ch-result// 某个goroutine提前关闭了channel// 其他goroutine还在往里发数据panicclose(ch)// 这里是bug}(input)}forr:rangech{fmt.Println(r)}}正确做法是用sync.WaitGroup等所有goroutine完成后再关闭channel或者用一个单独的goroutine负责关闭。packagemainimport(fmtsync)funcheavyCompute(valint)int{returnval*2}funcprocessData(inputs[]int){ch:make(chanint,10)varwg sync.WaitGroup// 启动多个worker goroutinefor_,input:rangeinputs{wg.Add(1)gofunc(valint){deferwg.Done()result:heavyCompute(val)ch-result// 不在这里关闭channel}(input)}// 单独启动一个goroutine等待所有worker完成// 然后关闭channelgofunc(){wg.Wait()close(ch)// 所有worker都完成了安全关闭}()// 主goroutine消费数据// channel关闭且缓冲区清空后自动退出循环forr:rangech{fmt.Println(r)}}funcmain(){processData([]int{1,2,3,4,5})}对比分析跟Java比Java没有channel这个概念线程间通信靠共享变量加锁或者用BlockingQueue。Go的channel把通信和同步合二为一代码更简洁。Java的BlockingQueue跟有缓冲channel很像但Java没有无缓冲channel的等价物要实现同步交接得用SynchronousQueue。跟Python比Python的queue.Queue是有缓冲队列跟有缓冲channel类似。Python没有无缓冲channel要实现goroutine间的同步交接得用Event或Condition。Python的queue.Queue是线程安全的但没有channel的方向限制。跟Rust比Rust的mpsc channel跟Go的channel类似但Rust区分了单生产者oneshot和多生产者mpsc类型系统更严格。Go的channel默认就是多生产者多消费者灵活但安全性差一些。总结这篇讲了channel的核心用法。无缓冲channel做同步通信发送和接收必须同时就绪。有缓冲channel做异步通信缓冲区没满就不阻塞。channel由发送方关闭用range遍历自动退出。单向channel限制方向防误操作。踩坑那节讲的向已关闭channel发送数据是最常见的channel panic记住用WaitGroup统一管理关闭时机。下一篇讲select语句。当你在多个channel之间等待数据时select是唯一的选择。它能同时监听多个channel哪个先来就处理哪个还能做超时控制。
RELATED

相关推荐

基于SpringBoot的山东非遗剪纸数字化展示与教学网站的设计与实现源码+文档

基于SpringBoot的山东非遗剪纸数字化展示与教学网站的设计与实现源码+文档

温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:本人主页置顶文章(点我)开头有 CSDN 平台…

📅 2026/10/19 17:38:00
抖音无水印下载实战:用 douyin-downloader 免费批量备份博主主页、合集与直播

抖音无水印下载实战:用 douyin-downloader 免费批量备份博主主页、合集与直播

抖音无水印下载实战:用 douyin-downloader 免费批量备份博主主页、合集与直播 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and …

📅 2026/9/8 23:38:45
一步步搞定抖音批量下载:douyin-downloader 免费工具从安装到进阶的完整记录

一步步搞定抖音批量下载:douyin-downloader 免费工具从安装到进阶的完整记录

一步步搞定抖音批量下载:douyin-downloader 免费工具从安装到进阶的完整记录 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and b…

📅 2026/9/26 23:06:23
MORE NEWS

更多资讯

📰

多智能体并非越多越好:Brooks定律下的AI Agent效率陷阱

前阵子我在做一个自动化分析工具,一开始脑热,把整个流程拆成了六个AI智能体:一个负责读数据,一个负责总结,一个负责提建议,一个负责审查,还有一个负责编排,最后还配了个"兜底&q…

📰

用Dify零代码实现双模型分栏同步流式输出,替代OpenWebUI

把 OpenWebUI 换成 Dify 做双模型分栏同步流式输出,是最近我搭内部演示工具时反复验证过的一条路。OpenWebUI 大家都很熟,界面简洁、多模型同屏输出也没问题,但真到业务场景里,我想要的是一个输入框发出去,两个模型同时…

📰

AI重塑神经科学:数据处理、模型选型与避坑实战指南

1. 从AI for Science说起:神经科学为什么是下一块硬骨头最近这几年,AI for Science从一个挂在嘴边的概念,逐渐变成了实打实的研究工具。蛋白质结构预测、材料筛选、基因组学这些方向,AI已经跑在前面,甚至成了不少实验室…

📰

7-Zip安装教程:从下载到使用的干净解压软件完全指南

说实话,现在电脑上想找个“干净”的解压软件,比找一部好剧还难。满屏的“高速下载”按钮、弹窗广告、捆绑安装,一不小心就把全家桶请进家门。所以当我看到“7-Zip安装教程”这个词条时,第一反应就是:终于有人在认真推荐…

📰

文件包含漏洞实战:从LFI到RFI再到日志注入与图片马组合利用

文件包含漏洞这种问题,刚接触 Web 安全时我总觉得它很低级:无非就是 include 了一个用户传进来的参数。直到在一套开源靶场里把文件包含模块从低到高全刷了一遍,才发现里面的细节比想象中多很多。本地包含、远程包含、伪协议读源码、日志注入…

📰

基于书评文本的推荐系统:Java实现标签提取与相似度匹配

简介:一套基于网络书评文本内容的个性化书籍推荐系统设计源码,面向Java开发者、推荐系统研究者及自然语言处理入门者,解决如何从大量书评中甄别有效评论、构建书籍标签集并计算标签关联度,以实现内容精准匹配的问题。压缩包共40个…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬