chan

为什么 chan 和 <code> 不能互换使用
因为 Go 编译器会严格检查通道方向,把单向通道当作独立类型处理。chan 只能发送,<code> 只能接收,两者类型不兼容,强行赋值会报错:<code>cannot use ch (type chan。
这种设计不是语法糖,而是编译期强制的职责隔离。比如一个函数只负责发数据,就该只拿到 chan;另一个只消费,就只该持有 <code>。一旦传错方向,代码根本过不了编译。
- 声明单向通道必须用显式语法:
ch := make(chan int)是双向的;sendOnly := make(chan 或 <code>recvOnly := make( 才是单向的 - 函数参数中常见写法:
func producer(ch chan 表明这个函数只往里塞数据,调用者放心传入任何可发送的通道 - 从双向转单向可以隐式转换(如
ch→chan),但反向不行
close() 只能对双向或发送端通道调用
对 类型调用 <code>close() 会编译失败:invalid operation: close(recvOnly) (receive-only channel cannot be closed)。这是语言层面对“谁负责结束通信”的硬约束。
典型模式是:生产者关闭发送端,消费者通过 value, ok := 检测是否已关闭;接收端永远不该、也不能主动关通道。
- 正确:生产者函数内
close(ch)(前提是ch是chan 或双向) - 错误:在只接收的函数里写
close(ch),直接报错 - 即使你用
interface{}包装通道,运行时也无法绕过这个检查——编译期就卡死了
单向通道如何避免 goroutine 泄漏和逻辑混乱
当多个 goroutine 共享一个双向通道时,容易出现发送方提前退出但接收方还在等、或接收方早关了但发送方还在发的情况。单向通道配合明确的接口契约,能提前暴露这类问题。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
例如,一个工作池中,任务分发器拿到 chan,worker 拿到 <code>。如果 worker 错误地试图往通道里写结果,代码根本编译不过;如果分发器意外尝试读取,同样被拦住。
- 泄漏常见场景:goroutine 向已无接收者的
chan 发送数据,永久阻塞 - 单向声明不会阻止阻塞,但它让“谁该停、谁该等”更清晰——发送端负责关,接收端负责判
ok - 实际工程中,建议所有跨 goroutine 的通道参数都声明为单向,哪怕底层是双向创建的
别忽略类型转换的隐含代价
虽然 chan int 可以隐式转成 chan,但这个转换发生在每次函数调用时,不是零成本。尤其在高频循环中传递通道,可能产生轻微逃逸或额外指针操作。
更关键的是,这种隐式转换掩盖了真实的数据流向意图。如果你看到一个函数接收 chan int 却只做发送,说明接口设计有问题——应该直接要求 chan,让调用方一眼看懂契约。
- 不要为了省几个字符写
func f(ch chan int),再靠注释说明“只发不收” - 接收方如果需要同时读写(极少见),才用双向;绝大多数场景,单向才是默认选择
- IDE 和静态分析工具(如
staticcheck)能基于单向类型发现更多潜在 misuse
单向通道的价值不在“多了一种写法”,而在把并发职责拆进类型系统里——不是靠文档约定,而是靠编译器盯着你别越界。一旦习惯写 chan 和 <code>,很多死锁和泄漏问题会在写完第一行就暴露出来。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










