直接回答:原则:单个变量的读写计数用 atomic,跨多操作、多变量的不变式用 Mutex。atomic 基于 CPU 原子指令(x86 的 LOCK 前缀),不挂起 goroutine,单次开销低于加锁;但它只保证单个操作不可分割,管不住「读—判断—写」复合逻辑。Mutex 把临界区串行化,表达力强,代价是竞争时 goroutine 阻塞切换。
var hits atomic.Int64 // 1.19+ 类型化 API
hits.Add(1)
mu.Lock()
if balance[from] < n { return false } // 读-判断-写须整体原子
balance[from] -= n
mu.Unlock()
实践要点:其一,转账这类多对象一致性必须上锁。其二,旧 API(atomic.AddInt64)要求操作数 8 字节对齐,32 位平台会 panic,新代码一律用 atomic.Int64/Bool/Pointer。其三,多核写同一缓存行的不同变量会伪共享,压垮 atomic 性能,可 padding 隔离。其四,读多写少时 RWMutex 往往更好维护——别为省几十纳秒写自制锁。
追问方向:CAS 的 ABA 问题是什么?Mutex 的饥饿模式如何切换?(约 576 字)