如果我使用锁,我的算法还能保持无锁状态吗?


10

无锁的一个常见定义是至少有一个进程可以取得进展。1个

如果我有一个简单的数据结构(例如队列),并受锁保护,那么一个进程始终可以取得进展,因为一个进程可以获取锁,执行所需操作并释放它。

那么它符合无锁的定义吗?


1参见例如M. Herlihy,V。Luchangco和M. Moir。无障碍同步:以双端队列为例。在《分布式计算》,2003年。“如果仅确保某些线程始终在进步,那么它将是无锁的”。


3
我一直都理解“无锁”来描述不使用锁的数据结构和算法集,而只是一组定义好的原子存储操作。如drdobbs.com/parallel/writing-lock-free-code-a-corrected-queue/...
保罗·约翰逊

Answers:


16

这不是无锁的定义。

如果可以保证进度,那么您将拥有无死锁,并且如果最终完成每个请求,那么您将拥有无饥饿,但并非无锁。

我怀疑您的简单示例是否确实提供了此功能。当涉及多个锁时,您需要锁层次结构等才能真正保证进度。


1
我正在使用M. Herlihy的定义。实现高度并发数据对象的方法。编程语言和系统交易,1993年。“无锁定条件保证,即使任意终止故障或其他进程造成的延迟,某些进程也总是会取得进展”
Joe Pension

12
@Joe:这不是一个定义,它描述了一个含义。当心逆逻辑的谬误。
Ben Voigt 2012年

2
也可以引用M. Herlihy,V。Luchangco和M. Moir。无障碍同步:以双端队列为例。在《分布式计算》,2003年。“如果仅确保某些线程始终在进步,那么它将是无锁的”。
Joe Pension

1
还有没有饥饿的问题,它比没有死锁的问题更为具体(无论其他进程做什么,每个进程都可以执行)请注意,CaS循环(基于原子基元)不是
棘轮异常

5
@Joe:如果世界其他地区将该属性称为无死锁,那么我将使用该术语。不,您的简单示例并非没有死锁。为了保证没有死锁,您不仅需要同步,而且要保证没有线程在持有锁的同时执行任何阻塞操作。“做它想做的事情”非常含糊,似乎不能提供这种保证。
Ben Voigt 2012年

11

我已经学习了《多处理器编程的艺术》1,其文字缺乏清晰度,就像您所参考的书一样。以下是TAMPP的一些引文:

引用1(无锁的定义)

如果一个方法可以无限期地保证某些方法调用以有限的步数完成,则该方法是无锁的。

引用2(非阻塞的定义)

完全不需要等待总计方法的等待调用来等待另一个等待调用完成。

引用3(声称无锁是非阻塞的)

无等待和无锁的非阻塞进度条件可确保整个计算进度,而与系统如何调度线程无关。

问题在于,引用3中的声明显然不遵循引用1中的定义。正如已经提到的,同步队列似乎满足引用1:无限地,某些方法通常会成功获取锁定并完成。

特别要注意引号3中非常模糊的短语:“与系统如何调度线程无关”。这不是由任何一种“线程调度系统”的形式描述的前面,所以我们只剩根据我们的定义是什么成见来重建它的属性应该是指:

  1. 系统总是执行某个线程的指令;
  2. 它可能永远不会执行任何给定线程的指令;
  3. 所有线程都在调用正在考虑的方法。

在这样的系统上,阻塞方法不能是无锁的:如果持有锁的线程再也没有计划执行,那么将没有其他线程可以在有限的步骤中完成其方法调用。一些线程正在执行该方法的步骤。对于一个更现实的系统(确实保证最终为每个线程分配CPU时间),该定义必须明确包含nonblocking属性:

更正了无锁的定义

如果方法是非阻塞的,则它是无锁的,此外,它还保证无限次地某些方法调用以有限的步骤数完成。

1 Maurice Herlihy,Nir Shavit,《多处理器编程的艺术》,Elsevier 2008,第58-60页。


1
引用1的措词确实很奇怪。“无限频繁”是什么意思?显然与“始终”有所不同,因此可以确定方法在“某些”情况下永远不会返回吗?
绿巨人

是的,不精确的语言比比皆是。无论如何,“经常”是什么?我认为它们的意思是“在无限的执行历史中,此特定事件无限次发生”。
Marko Topolnik '16

5

术语并不总是一致的,但是我认为重要的是要提出关于所提议的算法或系统的以下问题:

  1. 是否有任何事件序列,即使所有线程都被允许使用它们的所有CPU时间都可能使线程陷入彼此的等待中[如果这样,则不是没有死锁的]
  2. 如果一个线程在任意长时间内被阻塞,那么是否有可能使其他线程停滞或在任意长时间内损害系统运行[如果是这样,那不是非阻塞的]。
  3. 是否有某种至少在理论上可能的线程调度组合,该组合可能导致所有线程在使彼此的工作无效的情况下重复重试相同的操作,而没有任何进展[如果这样,那不是无锁的]
  4. 如果为某些线程提供了相对于另一线程足够的CPU时间,它们是否可以迫使后一个线程无限期地重试其操作[如果是这样,则它不是没有等待的时间]。

无锁算法的许多重要意义并不在于它们比非无锁算法要快,而是在于如果线程被铺设,它们不容易死掉[请注意,这样的保证只需要算法是非阻塞的,但是所有无锁算法都是]。无锁算法可以使用锁,但是仅当锁获取尝试包括超时以及算法以确保某人总是有进步的时候(例如,算法可以使用CompareExchange循环作为主要算法)仲裁方法,但是当争用似乎很高时,使用锁来仲裁访问;如果锁持有的时间似乎太长,其他线程可以决定放弃使用该锁的工作,而是创建一个新锁。CompareExchange,让客户放弃该锁不会损害系统的一致性,尽管这可能意味着持有旧锁的代码将无法完成任何工作,除非它也放弃了旧锁并与新锁保持一致。


这与标准术语不同:2.表示非阻塞的标准含义,而3.表示锁自由。
Marko Topolnik '16

我看到过不一致的术语用法,而且我不知道“官方”标准。最重要的是,算法可能提供不同的保证,并且使用算法提供足以满足应用程序要求的保证很重要。许多论文只涉及上述部分保证,但在某些情况下,每种保证比其他满足要求的保证能够更轻松地满足应用要求。
超级猫

我认为已经达成共识,即“多处理器编程的艺术”中提出的术语是“标准的”。
Marko Topolnik '16

@MarkoTopolnik:我将编辑帖子以适合当时的情况。你喜欢新版本吗?
超级猫

酷,非常好。
Marko Topolnik '16

4

您必须查看上下文中引用的“定义” :

实现共享数据结构的传统方式是使用互斥(锁定)以确保并发操作不会相互干扰。锁定在软件工程,容错和可伸缩性方面有许多缺点(请参见[8])。作为回应,研究人员研究了多种不采用互斥替代同步技术。如果同步技术可确保每个线程在面对其他线程的任意延迟(甚至失败)的情况下继续取得进展,则无需等待。如果它仅确保某些线程始终在进行,则它是无锁的。

您正在使用锁进行互斥,因此,这并不是他们所说的无锁技术。


2

如果我使用锁,我的算法还能保持无锁状态吗?

可以,但是取决于算法。

如果我有一个简单的数据结构(例如队列),并受锁保护,那么一个进程始终可以取得进展,因为一个进程可以获取锁,执行所需操作并释放它。

那么它符合无锁的定义吗?

注意本身

如果“做它想做的”步骤不涉及获取其他任何锁,并且可以保证在有限的时间内完成锁,那么算法的这一特定部分将无死锁。

但是,如果不满足这些先决条件,则至少有可能出现死锁...


在对《多处理器编程的艺术》一书中的文字进行研究之后,我得出的结论是,正确定义了互斥锁后,互斥锁肯定会使无锁的定义无效。我已在此页面上添加了答案以澄清这一点。
Marko Topolnik '16

1

由于以下原因,您给出的示例并非无锁。

支持一个线程获取锁,并且OS调度程序将线程挂起无限单独的时间段,那么所有线程都无法取得进展,因为没有人可以获取被挂起线程获取的锁。

一般来说,使用锁的算法不是无锁的。

请注意,无死锁和无锁是两个不同的概念。无死锁意味着没有死锁的可能性,但是可能存在活锁,这可能会阻止整个系统的进步。自由锁定比它更强,因为它意味着系统中的某些线程总是以有限的步骤数来取得进展。


看一下Wikipedia上一个更仔细的定义:“一种算法是无锁的,只要它满足以下要求:当程序线程运行足够长的时间时,至少有一个线程能够进步。这不包括暂停线程的情况。停止中的进展也包括障碍自由,而不是锁定自由
Marko Topolnik '16

@MarkoTopolnik您的评论根本没有意义。无锁涵盖了无障碍。无锁的任何物品都必须无障碍。您提供的定义并不排除暂停线程。
朝然

请注意区分“有意义”与“正确”。从我随后的回答可以看出,我的评论是不正确的。但是维基百科的定义也是错误的,或者至少是模棱两可的。
Marko Topolnik '16

@MarkoTopolnik由于您承认自己的评论不正确,因此应将其删除,以免使其他读者感到困惑。维基百科通常是不正确或模棱两可的。你应该找到像学术论文,如“锁定自由”微妙的定义cs.rochester.edu/~scott/papers/2006_PPoPP_synch_queues.pdf(2.1节的无锁就是定义)
超然

是的,将非阻塞属性作为锁自由定义的一部分是实现此目的的一种方法。在我的答案的较早版本中已说明了这一点。
Marko Topolnik '16
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.