为什么大多数互斥量实现不公平?


11

我的理解是,互斥锁的大多数流行实现(例如C ++中的std :: mutex)都不保证公平性 -也就是说,它们不保证在争用的情况下线程将按其获取锁的顺序进行操作。称为lock()。实际上,甚至有可能(尽管希望很罕见)在争用较高的情况下,某些等待获取互斥锁的线程可能永远无法获取它。

对我而言,这似乎是无益的行为-在我看来,公平的互斥体所产生的行为更符合程序员的期望/期望。

给出互斥量通常不被实现为公平的原因是“性能”,但我想更好地理解这意味着什么-特别是,放宽互斥量的公平性要求如何提高性能?看起来“公平”的互斥锁似乎是微不足道的实现-只需将lock()将调用线程附加到互斥锁的链表的末尾,然后再将其置于睡眠状态,然后使unlock()从下一个弹出线程相同列表的开头并将其唤醒。

我在这里缺少什么互斥体实现的见解,可以解释为什么认为为了提高性能而牺牲公平性是值得的吗?


1
每个互斥锁的链接列表必须是共享数据结构,对吗?那么,如何在不降低性能的情况下防止数据竞争呢?
user3543290

我认为,使用无锁的链表机制。为了找到下一个唤醒线程,不公平的互斥使用什么数据结构?
杰里米·弗里斯纳

1
您必须查一下,但是无锁链表可以保证公平吗?我认为您会发现很难实现并发编程中的公平性。
user3543290

Answers:


7

吉姆·索耶(Jim Sawyer)的答案指向一个答案:当线程的优先级不同时,“公平”行为将是不正确的。当您有多个可以运行的线程时,优先级最高的线程通常是应运行的线程。

但是,您应该了解操作系统实现的一个鲜为人知的秘密,即操作系统偶尔会通过劫持用户线程来以用户身份运行代码。出于安全原因,大多数执行此操作的操作系统仅在线程被阻塞时执行此操作。操作系统完成后,线程将被重新挂起,这通常具有将线程移到等待队列后面的效果。

一个典型的示例是Unix中的信号处理程序,VMS中的异步系统陷阱或Windows NT中的异步过程调用。这些本质上都是同一件事:操作系统需要通知用户进程发生了某些事件,这是通过在用户空间中运行代码来处理的。

许多操作系统服务(例如异步I / O)通常在此工具的顶部实现。

另一个示例是该过程是否在调试器的控制下。在这种情况下,由于各种原因,调试系统可能会将代码作为用户任务执行。


4

“优先倒置”是不公平的原因之一。低优先级的进程将击中锁定的互斥锁并进入睡眠状态。然后更高优先级的进程将其击中并进入休眠状态。互斥锁解锁后,接下来哪个进程应该获得锁定?


欢迎来到站点,并感谢您回答已经存在一段时间而又没有答案的问题!您的答案当然是正确的,但我认为它可能有更多细节。
David Richerby

3

在所有其他条件相同的情况下,一个公平的互斥锁将比不公平的互斥锁花费更多的生命。因为释放不公平互斥量的线程始终可以将其解锁。但是,释放公平互斥量的线程只能在侍者队列为空时将其解锁。否则,释放线程必须为了下一个线程(也就是服务员队列中的第一个线程)而将互斥锁保持锁定状态,然后将其出队并唤醒。互斥锁至少在将新唤醒的线程安排在CPU上之前一直保持锁定状态,如果当前有许多可运行的线程,则可能需要很长时间。

而且,如果释放线程立即尝试重新获取相同的互斥锁,则它必须将自己置于服务员队列的后面并进入睡眠状态。如果线程没有释放互斥锁,就不会发生这种情况。因此,这会激励更长的“更贪婪”的关键部分。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.