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