go语言中sync是提供同步原语的包,非函数或调度器;mutex通过原子操作实现锁,仅竞争激烈时才触发goroutine阻塞与唤醒,waitgroup和rwmutex同理,且所有sync类型均不可复制。

Go 语言里没有叫 “Sync 函数” 的东西,sync 是一个包,不是函数;它不负责“调度”,而是提供同步原语来防止并发错误。
sync.Mutex 的 Lock/Unlock 不是调度器调用的,而是直接操作锁状态
很多人误以为 Lock() 会“让出 CPU”或“触发调度”,其实不然:Lock() 在快速路径下只是原子地修改 state 字段(比如用 atomic.CompareAndSwapInt32),成功就直接进入临界区;失败才会走到慢路径,调用运行时的信号量等待逻辑(sema),这时才可能真正阻塞并让出 P。
- 锁竞争不激烈时,
Lock()几乎无开销,不涉及 goroutine 切换 - 锁被占用且有多个等待者时,运行时会把 goroutine 挂起,交由调度器后续唤醒——但这和你主动调用
runtime.Gosched()完全不同 -
Unlock()同样先走原子操作,仅当存在等待者时才唤醒一个(或广播),也不触发调度决策
sync.WaitGroup 的 Add/Done/Wait 和调度无关,只做计数与通知
WaitGroup 本质是带信号量的计数器:Add(n) 增加计数,Done() 原子减一,Wait() 在计数为 0 前挂起当前 goroutine。它本身不控制 goroutine 执行顺序,也不干预调度器行为。
-
Wait()内部用的是runtime_semacquire,和Mutex底层共享同一套信号量机制 - 如果你在
Wait()前没调用足够次数的Add(),会导致 panic:panic: sync: negative WaitGroup counter -
Done()必须在 goroutine 内调用,不能在Wait()返回后补调——计数归零后,再Add()是未定义行为
sync.RWMutex 的 RLock/RLock 性能差异来自读写分离策略
读多写少场景下,RWMutex 比 Mutex 高效,但代价是更复杂的状态管理:多个 RLock() 可以并发通过,而 Lock() 必须等所有读锁释放。
- 写操作调用
Lock()时,若已有读锁持有,它不会立即抢锁,而是先进入“写等待队列”,等所有活跃读锁退出后再上锁 - 频繁混用
RLock()和Lock()可能导致写饥饿(writer starvation),尤其在持续高并发读的情况下 -
RLock()后必须配RUnlock(),漏掉会导致后续写操作永久阻塞——这点比Mutex更容易踩坑
真正容易被忽略的是:所有 sync 类型都**不可复制**。一旦结构体字段含 sync.Mutex 等,就不能用 = 赋值或作为 map key;传参务必传指针。否则运行时会 panic:fatal error: copy of unlocked Mutex。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











