恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Channel详解:无缓冲vs有缓冲的选择

  • 首页
  • 资讯中心
  • /
  • Channel详解:无缓冲vs有缓冲的选择

相关资讯

基于SpringBoot的山东非遗剪纸数字化展示与教学网站的设计与实现源码+文档 2026/8/15 2:11:28
抖音无水印下载实战:用 douyin-downloader 免费批量备份博主主页、合集与直播 2026/8/15 2:11:28
一步步搞定抖音批量下载:douyin-downloader 免费工具从安装到进阶的完整记录 2026/8/15 2:11:28

最新资讯

MathorCup数模竞赛:从赛题结构解析到团队能力匹配的快速选题策略
19套热门表情包系统整理与高效管理全攻略
终极DDrawCompat完整指南:让老DirectX游戏在现代Windows上重获新生
微信读书笔记插件 WeReader 实测:3 步导出 Markdown 笔记,阅读效率提升看得见
Java Stream API实战:List对象属性聚合计算(求和、最大、最小、平均值)
电脑开机慢?从启动项到硬盘,4步排查让开机回到十秒时代

今日推荐

内景 空间站内部 中国空间站 太空 内仓
重新定义数据接口:3个突破性场景让通达信数据读取更智能
5大网络安全实操平台,免费练手入门,轻松掌握攻防技能

本周热门

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁
如何快速生成中国车牌图片:Python开源工具完整指南
当 LLM 遇见大文档:主流开源项目如何处理上下文超限

本月精选

如何用DamaiHelper实现演唱会门票的智能自动化抢购:完整技术解决方案指南
第4篇:59 倍性能差距的索引瓶颈定位——一次教科书级的全表扫描调优
终极歌词批量下载神器:5分钟解决离线音乐库歌词同步难题

Channel详解:无缓冲vs有缓冲的选择

发布时间:2026/8/15 2:11:28
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哪个先来就处理哪个还能做超时控制。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号