直接回答:无竞争时 mutex 加解锁是两次原子操作(~20ns),channel 收发要复制数据、维护等待队列、可能触发 goroutine 挂起唤醒(~100ns 起),慢 3~5 倍;但 channel 的值在“传递所有权”——选择标准是语义而非微基准:保护共享状态用 mutex,传递数据所有权或做协调用 channel,即“share memory by communicating” 只适用于所有权转移场景。
展开解析:性能差异的来源拆解:mutex 的 happy path 是无竞争 CAS,竞争后走自旋加信号量挂起;channel 每次操作都有数据拷贝(发送方栈到队列槽到接收方栈,指针类型则拷贝指针加写屏障)、内置锁保护队列、阻塞时完整 gopark/goready 调度。所以大结构体走 channel 拷贝成本翻倍——传指针(注意所有权随之转移,发送后别再碰)。选择决策树:计数器/配置/缓存这类“大家都要读写同一份”——sync.Mutex 或 atomic(单变量 CAS 最快);任务分发、结果汇聚、事件流这类“东西从 A 到 B”——channel;扇出扇入结构固定——WaitGroup;高性能读多写少——sync.RWMutex 或 atomic.Value。反模式:用 channel 当锁使(buffered(1) 当互斥锁——能跑但语义糊涂且更慢);用 mutex 保护再经 channel 传同一对象(双重同步,必有一个多余)。压测差异在高竞争下才显著,低并发场景选可读性——语义清晰的代码比省 50ns 重要。真到纳秒级战场(高频交易网关)还有 lock-free 队列等选项,但那是另一个工程量级。
追问方向:为什么无缓冲 channel 能当同步点用?写屏障在 channel 拷贝中起什么作用?
(约 470 字)