“benaphores”值得在现代操作系统上实现吗?
-
06-07-2019 - |
题
在我作为BeOS程序员的日子里,我读到了本文由Benoit Schillings撰写,描述了如何创建”benaphore“:一种使用原子变量来强制执行关键部分的方法,该部分避免了在共同中获取/释放互斥的需要(不争议案例。
我认为这很聪明,似乎你可以在任何支持原子增量/减量的平台上做同样的技巧。
另一方面,这看起来像是可以很容易地包含在标准互斥实现本身中的东西......在这种情况下,在我的程序中实现这个逻辑将是多余的,并且不会带来任何好处。
有人知道现代锁定API(例如pthread_mutex_lock()/ pthread_mutex_unlock())是否在内部使用此技巧?如果没有,为什么不呢?
解决方案
您的文章描述的内容现在已经普遍使用。大多数情况下,它被称为“关键部分 ;它包含一个互锁变量,一堆标志和一个内部同步对象(Mutex,如果我没记错的话)。通常,在几乎没有争用的场景中,Critical Section完全在用户模式下执行,而不涉及内核同步对象。这保证了快速执行。当争用很高时,内核对象用于等待,这会释放时间片导电,以便更快地进行周转。
通常,在这个时代实现同步原语的意义非常小。操作系统附带了大量此类对象,并且它们在比单个程序员可以想象的更广泛的场景中进行了优化和测试。实际上,需要花费数年的时间来发明,实现和测试良好的同步机制。这并不是说尝试没有价值:)
其他提示
Java的 AbstractQueuedSynchronizer
(及其兄弟 AbstractQueuedLongSynchronizer
)的工作方式类似,或者至少它可以类似地实现。这些类型构成了Java库中几个并发原语的基础,例如 ReentrantLock
和 FutureTask
。
它通过使用原子整数来表示状态。锁可以将值0定义为已解锁,将1定义为已锁定。任何希望获取锁的线程都会尝试通过原子 compare-and-set 操作将锁定状态从0更改为1;如果尝试失败,则当前状态不为0,这意味着该锁由其他某个线程拥有。
AbstractQueuedSynchronizer
还通过维护 CLH队列来帮助等待锁定和条件的通知,这些是表示线路的无锁链接列表。等待获取锁定或通过条件接收通知的线程。这样的通知将等待条件的一个或所有线程移动到等待获取相关锁的队列的头部。
这个机器的大多数可以用表示状态的原子整数以及每个等待队列的几个原子指针来实现。实际调度哪些线程将争用检查和更改状态变量(例如,通过 AbstractQueuedSynchronizer#tryAcquire(int)
)超出了这样一个库的范围并落到主机系统的调度器。