无锁的一个常见定义是至少有一个进程可以取得进展。1个
如果我有一个简单的数据结构(例如队列),并受锁保护,那么一个进程始终可以取得进展,因为一个进程可以获取锁,执行所需操作并释放它。
那么它符合无锁的定义吗?
1参见例如M. Herlihy,V。Luchangco和M. Moir。无障碍同步:以双端队列为例。在《分布式计算》,2003年。“如果仅确保某些线程始终在进步,那么它将是无锁的”。
无锁的一个常见定义是至少有一个进程可以取得进展。1个
如果我有一个简单的数据结构(例如队列),并受锁保护,那么一个进程始终可以取得进展,因为一个进程可以获取锁,执行所需操作并释放它。
那么它符合无锁的定义吗?
1参见例如M. Herlihy,V。Luchangco和M. Moir。无障碍同步:以双端队列为例。在《分布式计算》,2003年。“如果仅确保某些线程始终在进步,那么它将是无锁的”。
Answers:
这不是无锁的定义。
如果可以保证进度,那么您将拥有无死锁,并且如果最终完成每个请求,那么您将拥有无饥饿,但并非无锁。
我怀疑您的简单示例是否确实提供了此功能。当涉及多个锁时,您需要锁层次结构等才能真正保证进度。
我已经学习了《多处理器编程的艺术》1,其文字缺乏清晰度,就像您所参考的书一样。以下是TAMPP的一些引文:
如果一个方法可以无限期地保证某些方法调用以有限的步数完成,则该方法是无锁的。
完全不需要等待总计方法的等待调用来等待另一个等待调用完成。
无等待和无锁的非阻塞进度条件可确保整个计算进度,而与系统如何调度线程无关。
问题在于,引用3中的声明显然不遵循引用1中的定义。正如已经提到的,同步队列似乎满足引用1:无限地,某些方法通常会成功获取锁定并完成。
特别要注意引号3中非常模糊的短语:“与系统如何调度线程无关”。这不是由任何一种“线程调度系统”的形式描述的前面,所以我们只剩根据我们的定义是什么成见来重建它的属性应该是指:
在这样的系统上,阻塞方法不能是无锁的:如果持有锁的线程再也没有计划执行,那么将没有其他线程可以在有限的步骤中完成其方法调用。一些线程正在执行该方法的步骤。对于一个更现实的系统(确实保证最终为每个线程分配CPU时间),该定义必须明确包含nonblocking属性:
如果方法是非阻塞的,则它是无锁的,此外,它还保证无限次地某些方法调用以有限的步骤数完成。
1 Maurice Herlihy,Nir Shavit,《多处理器编程的艺术》,Elsevier 2008,第58-60页。
术语并不总是一致的,但是我认为重要的是要提出关于所提议的算法或系统的以下问题:
无锁算法的许多重要意义并不在于它们比非无锁算法要快,而是在于如果线程被铺设,它们不容易死掉[请注意,这样的保证只需要算法是非阻塞的,但是所有无锁算法都是]。无锁算法可以使用锁,但是仅当锁获取尝试包括超时以及算法以确保某人总是有进步的时候(例如,算法可以使用CompareExchange
循环作为主要算法)仲裁方法,但是当争用似乎很高时,使用锁来仲裁访问;如果锁持有的时间似乎太长,其他线程可以决定放弃使用该锁的工作,而是创建一个新锁。CompareExchange
,让客户放弃该锁不会损害系统的一致性,尽管这可能意味着持有旧锁的代码将无法完成任何工作,除非它也放弃了旧锁并与新锁保持一致。
如果我使用锁,我的算法还能保持无锁状态吗?
可以,但是取决于算法。
如果我有一个简单的数据结构(例如队列),并受锁保护,那么一个进程始终可以取得进展,因为一个进程可以获取锁,执行所需操作并释放它。
那么它符合无锁的定义吗?
注意本身。
如果“做它想做的”步骤不涉及获取其他任何锁,并且可以保证在有限的时间内完成锁,那么算法的这一特定部分将无死锁。
但是,如果不满足这些先决条件,则至少有可能出现死锁...
由于以下原因,您给出的示例并非无锁。
支持一个线程获取锁,并且OS调度程序将线程挂起无限单独的时间段,那么所有线程都无法取得进展,因为没有人可以获取被挂起线程获取的锁。
一般来说,使用锁的算法不是无锁的。
请注意,无死锁和无锁是两个不同的概念。无死锁意味着没有死锁的可能性,但是可能存在活锁,这可能会阻止整个系统的进步。自由锁定比它更强,因为它意味着系统中的某些线程总是以有限的步骤数来取得进展。