4-18.【OC】【锁】semaphore = 1 与 mutex 在语义上有什么本质区别?

4 阅读3分钟

本质区别可以总结为:Mutex 是“所有权制”,而 Semaphore 是“信号制”。


1. 所有权 (Ownership):谁领的证,谁能离

这是两者最根本的区别。

  • Mutex(互斥锁): 具有 Ownership。当一个线程成功获取 Mutex 后,它就变成了该锁的“主人”。只有这个主人能解开这把锁。

    • 语义: “这是我的私人房间,我进去后反锁了,必须我出来才能开锁。”
  • Semaphore(信号量): 没有 Ownership。它只是一个原子计数器。任何线程都可以对它进行 signal(加 1),即使这个线程之前从未执行过 wait

    • 语义: “这更像是一个限流器或红绿灯。任何人都可以去按那个变绿灯的按钮,不管是谁之前把它变红的。”

2. 优先级继承 (Priority Inheritance)

由于 Mutex 知道是谁持有了锁,它能解决优先级反转问题,而 Semaphore 不能。

  • Mutex 的智能: 如果高优先级线程 H 在等低优先级线程 L 释放锁,内核会感知到 L 拥有这把锁,于是临时提升 L 的优先级。
  • Semaphore 的盲目: 信号量内部不记录持有者。当高优先级线程 H 在 wait 时,内核只看到 H 被挂起了,但不知道该去“催”谁。如果此时有一个中等优先级的线程 M 抢占了 L,H 就会被无限期阻塞。

3. 递归性 (Recursion)

  • Mutex: 很多 Mutex 实现(如 NSRecursiveLock)支持递归加锁。因为它记录了所有者,所以它知道:“哦,又是这个线程在重复要锁,我让他过。”
  • Semaphore: 绝对不支持递归。如果你在持有信号量的情况下再次 wait,它只会简单地把计数器从 0 减到 -1 并把你挂起。你会自己把自己锁死。

4. 异常安全性 (Exception Safety)

在一些高级语言中,Mutex 与作用域(Scope)绑定。如果临界区内发生了异常,系统可以根据“所有权”自动帮已经死掉或退出的线程释放锁。

而信号量如果漏掉了 signal,该资源就永久丢失了,没有任何机制能自动恢复它,因为它没有“归属”概念。


5. 核心语义总结表

特性Mutex (互斥锁)Semaphore = 1 (信号量)
底层核心锁的所有权 (Ownership)计数器 (Counter)
谁能释放必须是加锁的那个线程任何线程都可以
优先级继承支持 (防范优先级反转)不支持
典型用途保护共享资源、保护临界区任务同步、限流、跨线程通知
递归调用可配置支持绝对不支持 (死锁)

实战建议:

  • 如果你是为了防止多个线程同时修改一个变量,请永远选择 os_unfair_lockMutex,而不是信号量。
  • 如果你是为了让线程 A 等待线程 B 完成某个任务(跨线程同步),或者限制同时请求接口的数量,那么 dispatch_semaphore 才是正确的工具。