count++ 输出随机数是因为其非原子性导致竞态:读取、加1、写回三步中可能被其他goroutine插入,造成覆盖。Go的-race检测器可可靠发现此问题;sync.Mutex需成对使用,推荐defer mu.Unlock();锁粒度宜小;读多写少用RWMutex;单整数操作优先atomic;Mutex为值类型,应传指针避免复制。

为什么 count++ 会输出随机数
因为 count++ 不是原子操作:读取、加 1、写回三步中间可能被其他 goroutine 插入。两个 goroutine 同时读到 count == 0,各自算出 1,再写回,结果还是 1。Go 的 -race 检测器能直接报出 data race 错误,这是最可靠的判断依据。
sync.Mutex.Lock() 和 Unlock() 必须成对出现
漏掉 Unlock() 会导致后续所有 goroutine 在 Lock() 处永久阻塞;只调用 Unlock() 而没先 Lock() 会 panic(sync: unlock of unlocked mutex)。实际写法强烈推荐:
- 用
defer mu.Unlock()紧跟mu.Lock(),哪怕临界区里发生 panic 也能释放锁 - 不要把
Unlock()放在 if 分支末尾或 return 前——容易遗漏路径 - 锁的粒度越小越好,比如只包住
count++,而不是整个 goroutine 函数体
RWMutex 在读多写少场景下更高效
当共享资源被大量 goroutine 并发读、极少写时,sync.RWMutex 比普通 sync.Mutex 更合适。它的 R Lock() 允许多个 goroutine 同时读,而 Lock() 仍保证写独占。但注意:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
-
RUnlock()必须和R Lock()配对,否则 panic - 不能在持有
R Lock()期间调用Lock(),会死锁 - 频繁混合读写时,RWMutex 的调度开销可能反而更高
mutex 不是万能解药,别忽略原子操作
对单个整数变量做增减(如计数器),sync/atomic 包的 atomic.AddInt64(&count, 1) 比加锁更快、更轻量,且无死锁风险。但它只适用于简单类型和有限操作(+、&、compare-and-swap 等);一旦涉及多个字段联动更新(比如“余额减去金额,同时记录流水”),就必须用 sync.Mutex 或事务机制。
立即学习“go语言免费学习笔记(深入)”;
真正容易被忽略的是锁的生命周期管理:Mutex 是值类型,复制后互不影响;如果把它作为结构体字段嵌入,务必确保所有访问都通过同一实例——传指针,别传值。

















