很多人第一次学 Go 并发最懵的地方其实不是 goroutine。而是make(chanint)make(chanint,1)到底为什么一个会卡住 一个不会卡住你其实已经接近理解核心了。今天我按照真正底层逻辑给你彻底讲明白。一、chan 本质是什么chan本质goroutine 之间通信的管道例如goroutine A ↓ 发送数据 ↓ channel ↓ goroutine B ↓ 接收数据二、最核心问题通道到底有没有地方暂存数据三、无缓冲通道ch:make(chanstruct{})注意这里没有写容量所以它是无缓冲通道四、无缓冲通道真正本质重点无缓冲通道本质不能存数据也就是说发送和接收 必须同时发生五、为什么会阻塞因为通道里根本没有缓冲区所以发送方必须亲手把数据交给接收方六、最经典理解重点无缓冲通道像什么像面对面交接文件发送方“我把文件给你”接收方“我现在伸手接”七、如果没人接会怎样发送方只能一直举着文件等这就是阻塞八、代码例子重点packagemainimportfmtfuncmain(){ch:make(chanstruct{})gofunc(){-ch fmt.Println(goroutine 收到数据)}()ch-struct{}{}fmt.Println(main 继续执行)}九、执行流程真正核心第一步创建ch:make(chanstruct{})无缓冲通道。此时通道不能存数据第二步开启 goroutinegofunc(){-ch}()goroutine未来会接收数据。但注意不一定立刻执行十、Go 调度器重点很多新人这里会误解。你写gofunc()不代表立刻执行只是告诉 Go “帮我安排一个协程”什么时候真正运行调度器决定。十一、主 goroutine 继续往下执行ch-struct{}{}十二、这里为什么可能阻塞重点因为无缓冲通道不能存数据所以发送必须等待有人接十三、如果 goroutine 还没执行到 -ch主线程就会卡住等待十四、直到什么时候恢复直到goroutine真正执行-ch双方同时准备好。十五、这一刻发生什么发送方 ↓ 直接把数据交给接收方然后双方同时继续执行。十六、真正核心理解必须掌握无缓冲通道本质同步通信发送方必须等接收方。十七、带缓冲通道现在看ch:make(chanstruct{},1)这里容量 1十八、缓冲区本质是什么本质通道内部有个小仓库可以暂存数据十九、现在最大的区别来了重点无缓冲不能存 必须立刻交接带缓冲可以先存起来二十、类比重点带缓冲通道像什么像快递柜发送方把快递放进去 直接走接收方什么时候来取都行二十一、代码例子packagemainimportfmtfuncmain(){ch:make(chanstruct{},1)gofunc(){-ch fmt.Println(goroutine 收到数据)}()ch-struct{}{}fmt.Println(main 继续执行)}二十二、执行流程重点第一步创建make(chanstruct{},1)此时缓冲区容量 1第二步主线程执行ch-struct{}{}二十三、这里为什么不卡重点因为缓冲区现在还是空的所以数据直接放进去。二十四、发送完成后主线程立即继续。根本不需要等待接收方二十五、之后 goroutine 再接收-ch只是从缓冲区取数据二十六、真正核心区别必须理解无缓冲通道发送必须等接收因为没地方存有缓冲通道只要仓库没满 就能先存二十七、什么时候带缓冲也会阻塞例如ch:make(chanint,1)ch-1ch-2二十八、为什么第二次会卡住因为容量只有1第一次仓库满了第二次没地方放。于是阻塞等待直到有人接收。二十九、接收什么时候会阻塞例如ch:make(chanint,1)-ch三十、为什么会卡住因为缓冲区是空的没有数据。只能等发送方。三十一、总结成一句话重点发送阻塞条件缓冲区满接收阻塞条件缓冲区空三十二、为什么 struct{} 经常出现你会发现很多 Go 并发写chanstruct{}三十三、为什么不用 bool/int因为struct{}是空结构体三十四、空结构体特点不占内存三十五、所以它经常表示只通知 不传数据例如“任务完成了” “可以开始了”三十六、最经典场景例如控制 goroutine通知退出done:make(chanstruct{})通知开始start:make(chanstruct{})三十七、真正理解 channel最重要很多新人把 channel理解成队列其实更准确应该理解成goroutine 之间同步通信机制三十八、最后一句总结必须记住无缓冲通道不能存数据 发送必须等接收 属于同步通信带缓冲通道可以暂存数据 缓冲区不满发送不阻塞 更像队列阻塞条件操作阻塞条件发送缓冲区满接收缓冲区空真正核心channel 的本质 是 goroutine 之间的数据同步
Go 通道缓冲区(chan)详解
发布时间:2026/5/30 11:29:10
很多人第一次学 Go 并发最懵的地方其实不是 goroutine。而是make(chanint)make(chanint,1)到底为什么一个会卡住 一个不会卡住你其实已经接近理解核心了。今天我按照真正底层逻辑给你彻底讲明白。一、chan 本质是什么chan本质goroutine 之间通信的管道例如goroutine A ↓ 发送数据 ↓ channel ↓ goroutine B ↓ 接收数据二、最核心问题通道到底有没有地方暂存数据三、无缓冲通道ch:make(chanstruct{})注意这里没有写容量所以它是无缓冲通道四、无缓冲通道真正本质重点无缓冲通道本质不能存数据也就是说发送和接收 必须同时发生五、为什么会阻塞因为通道里根本没有缓冲区所以发送方必须亲手把数据交给接收方六、最经典理解重点无缓冲通道像什么像面对面交接文件发送方“我把文件给你”接收方“我现在伸手接”七、如果没人接会怎样发送方只能一直举着文件等这就是阻塞八、代码例子重点packagemainimportfmtfuncmain(){ch:make(chanstruct{})gofunc(){-ch fmt.Println(goroutine 收到数据)}()ch-struct{}{}fmt.Println(main 继续执行)}九、执行流程真正核心第一步创建ch:make(chanstruct{})无缓冲通道。此时通道不能存数据第二步开启 goroutinegofunc(){-ch}()goroutine未来会接收数据。但注意不一定立刻执行十、Go 调度器重点很多新人这里会误解。你写gofunc()不代表立刻执行只是告诉 Go “帮我安排一个协程”什么时候真正运行调度器决定。十一、主 goroutine 继续往下执行ch-struct{}{}十二、这里为什么可能阻塞重点因为无缓冲通道不能存数据所以发送必须等待有人接十三、如果 goroutine 还没执行到 -ch主线程就会卡住等待十四、直到什么时候恢复直到goroutine真正执行-ch双方同时准备好。十五、这一刻发生什么发送方 ↓ 直接把数据交给接收方然后双方同时继续执行。十六、真正核心理解必须掌握无缓冲通道本质同步通信发送方必须等接收方。十七、带缓冲通道现在看ch:make(chanstruct{},1)这里容量 1十八、缓冲区本质是什么本质通道内部有个小仓库可以暂存数据十九、现在最大的区别来了重点无缓冲不能存 必须立刻交接带缓冲可以先存起来二十、类比重点带缓冲通道像什么像快递柜发送方把快递放进去 直接走接收方什么时候来取都行二十一、代码例子packagemainimportfmtfuncmain(){ch:make(chanstruct{},1)gofunc(){-ch fmt.Println(goroutine 收到数据)}()ch-struct{}{}fmt.Println(main 继续执行)}二十二、执行流程重点第一步创建make(chanstruct{},1)此时缓冲区容量 1第二步主线程执行ch-struct{}{}二十三、这里为什么不卡重点因为缓冲区现在还是空的所以数据直接放进去。二十四、发送完成后主线程立即继续。根本不需要等待接收方二十五、之后 goroutine 再接收-ch只是从缓冲区取数据二十六、真正核心区别必须理解无缓冲通道发送必须等接收因为没地方存有缓冲通道只要仓库没满 就能先存二十七、什么时候带缓冲也会阻塞例如ch:make(chanint,1)ch-1ch-2二十八、为什么第二次会卡住因为容量只有1第一次仓库满了第二次没地方放。于是阻塞等待直到有人接收。二十九、接收什么时候会阻塞例如ch:make(chanint,1)-ch三十、为什么会卡住因为缓冲区是空的没有数据。只能等发送方。三十一、总结成一句话重点发送阻塞条件缓冲区满接收阻塞条件缓冲区空三十二、为什么 struct{} 经常出现你会发现很多 Go 并发写chanstruct{}三十三、为什么不用 bool/int因为struct{}是空结构体三十四、空结构体特点不占内存三十五、所以它经常表示只通知 不传数据例如“任务完成了” “可以开始了”三十六、最经典场景例如控制 goroutine通知退出done:make(chanstruct{})通知开始start:make(chanstruct{})三十七、真正理解 channel最重要很多新人把 channel理解成队列其实更准确应该理解成goroutine 之间同步通信机制三十八、最后一句总结必须记住无缓冲通道不能存数据 发送必须等接收 属于同步通信带缓冲通道可以暂存数据 缓冲区不满发送不阻塞 更像队列阻塞条件操作阻塞条件发送缓冲区满接收缓冲区空真正核心channel 的本质 是 goroutine 之间的数据同步