一般答案:
互斥锁是一个操作系统概念。提供互斥锁的操作系统必须确保这些互斥锁在该操作系统想要支持的所有硬件上正常工作。如果无法为特定硬件实现互斥锁,则操作系统无法在该硬件上提供互斥锁。如果操作系统需要存在互斥体才能正常工作,那么它根本无法支持该硬件。毫无疑问,操作系统如何为特定硬件实现互斥锁非常依赖于硬件,并且在操作系统及其支持的硬件之间存在很大差异。
详细答案:
大多数通用 CPU 都提供原子操作。这些操作旨在跨系统中的所有 CPU 内核进行原子操作,无论这些内核是单个 CPU 还是多个单独 CPU 的一部分。
只需两个原子操作atomic_or 和atomic_and,就可以实现锁。例如。想想
int atomic_or ( int * addr, int val )
它自动计算*addr = *addr | val 并在执行计算之前返回*addr 的旧值。如果*lock == 0 和多个线程调用atomic_or(lock, 1),那么其中只有一个会得到0 作为结果;只有第一个线程执行该操作。结果所有其他线程都得到1。获得0 的线程获胜,它拥有锁,所有其他线程注册一个事件并进入睡眠状态。
获胜者线程现在可以独占访问atomic_or 之后的部分,它可以执行所需的工作,一旦完成,它就会再次清除锁(atomic_and(lock, 0))并生成一个系统事件,即锁现在再次可用。
然后系统将在进入睡眠之前唤醒一个、部分或所有为此事件注册的线程,并重新开始争夺锁。任何一个被唤醒的线程都将赢得比赛,也可能没有,因为另一个线程更快,并且可能在atomic_and之间和其他线程甚至被唤醒之前抓住了锁,但这没关系,仍然正确,因为它仍然只有一个线程可以访问。所有未能获得锁的线程都重新进入休眠状态。
当然,现代系统的实际实现往往比这复杂得多,它们可能会考虑线程优先级(在锁竞争中可能首选高优先级线程),或者可能确保每个线程都在等待互斥锁最终也会得到它(存在防止线程总是失去锁竞争的预防措施)。互斥体也可以是递归的,在这种情况下,系统会确保同一个线程可以多次获得同一个互斥体而不会死锁,这需要一些额外的记录。
可能不用说,但原子操作是更昂贵的操作,因为它们需要系统内的内核同步它们的工作,这会降低它们的处理吞吐量。如果所有内核都在单个 CPU 上运行,它们可能会有些昂贵,但如果有多个 CPU,它们甚至可能非常昂贵,因为同步必须通过 CPU 总线系统进行,该总线系统将 CPU 相互连接,而这个总线系统通常不会以 CPU 速度级别运行。
另一方面,使用互斥体总是会减慢处理速度,因为如果多个线程需要同时访问以继续工作,则提供对资源的独占访问必须减慢处理速度。因此,对于实现互斥锁,这是无关紧要的。实际上,如果您可以仅使用原子操作而不是全功能互斥锁以线程安全的方式实现函数,那么您通常会获得明显的速度优势,尽管这些操作比普通操作更昂贵。