会,无缓冲channel要求收发同步,一方未就绪即阻塞goroutine;高频场景易卡死,应优先用带缓冲channel并合理设容量,配合select+default避免死锁。

会,而且影响非常直接——channel 用错地方、选错容量、阻塞模式不匹配,都会让 goroutine 大量挂起或调度失衡,性能掉得比不用 channel 还快。
无缓冲 channel 在高频通信时必然卡死
无缓冲 channel(即 make(chan int))要求发送和接收必须同步完成。只要一方没准备好,goroutine 就立即阻塞,进入等待队列。
常见于日志采集、事件广播等场景,但若 sender 和 receiver 处理速度不一致(比如 receiver 因磁盘 I/O 变慢),所有 sender 都会被拖住,goroutine 数暴涨,调度器压力陡增。
- 高频写入时,优先用带缓冲的
channel,缓冲大小按峰值吞吐预估(如make(chan int, 1024)) - 绝不把无缓冲
channel当“轻量消息队列”用;它本质是同步信号量,不是异步管道 - 用
select+default做非阻塞尝试,避免死等:select { case ch <- x: // 成功 default: // 缓冲满或无接收者,丢弃或降级处理 }
过度使用 channel 替代共享内存导致锁竞争转移
很多人以为 “用 channel 就不用锁”,结果把本该用 sync.Mutex 保护的计数器、状态机硬塞进 channel,反而引入更多 goroutine 切换和调度开销。
立即学习“go语言免费学习笔记(深入)”;
例如:每秒百万次计数,用 chan int 转发加法请求,比直接用 atomic.AddInt64 慢 10–50 倍,且 channel 自身有内存分配和锁(底层用 runtime.lock)。
- 简单状态读写、原子操作、固定结构体更新,优先用
sync/atomic或sync.Mutex -
channel的优势在解耦生产/消费节奏、跨 goroutine 控制流(如超时、取消、扇入扇出),不在替代基础并发原语 - 用
go tool trace查看Proc Status和Goroutine Analysis,如果大量 goroutine 停留在chan send或chan recv,基本就是误用了
关闭已关闭的 channel 或向已关闭 channel 发送数据会 panic
close(ch) 只能调用一次;重复关闭触发 panic: close of closed channel。向已关闭的 channel 发送数据也会 panic,但接收是安全的(返回零值+false)。
这问题常出现在多 goroutine 协同关闭的场景,比如 worker pool 中多个 goroutine 都想关同一个 done 通道。
- 只由明确的“发送方生命周期管理者”关闭 channel;接收方从不关
- 需要多方通知关闭时,改用
sync.Once包裹close,或用context.Context替代 - 不确定 channel 是否关闭?用
select+default配合recover是反模式;应靠设计规避,而非运行时兜底
最隐蔽的性能陷阱不是 channel 多慢,而是它让你误以为“已经解耦了”,结果 goroutine 堆积、GC 压力上升、延迟毛刺频发——而这些在本地小压测里根本看不出来。











