什么是无阻塞算法的例子,它不是无锁的?

时间:2017-09-29 13:25:57

标签: multithreading concurrency lock-free

所以锁定自由是指你保证整个系统取得进步,尽管有些线程可能会挨饿。因此,基于CAS的实施将提供此保证。

然后阻碍自由是指如果所有其他线程都被暂停,则保证一个线程完成。

你能给出一个无锁无障碍算法的简单例子吗?

谢谢!

2 个答案:

答案 0 :(得分:2)

我建议阅读论文introduced the term - 主要作者Herlihy在25年前介绍 wait-freedom 的概念时开始了这项业务。

锁定自由和阻碍自由之间的核心区别在于,如果两个或多个线程正在运行,后者不能保证进度(但如果只有一个正在运行)。

纸上的肉给你想要的东西,一个没有阻碍但没有锁定的出列的例子。

为了使它更简单,我将描述一个基于数组的堆栈,它以相同的方式运行,并且没有阻塞但没有锁定。

想象一下在数组顶部实现的堆栈,这样堆栈的零个或多个元素连续存储在数组的开头,然后是所有剩余位置的“null”元素。堆栈的每个元素都存储为元组:(val, seq),其中val是用户提供的值,seq是序列号,它是算法的关键(并且还避免了ABA问题 1 )。

要将元素推入堆栈,首先要找到堆栈中的最后一个元素(位置A[k-1]),该元素位于第一个 null 元素之前(位置{{1}) })。您尝试使用CAS增加A[k]的序列号(元素不会更改),如果成功,则尝试同时替换位置A[k-1]处的null元素的值并增加其序列数。如果CAS失败,则重试。

pop算法类似,但是以相反的顺序处理元素(递增第一个null元素的序列号,然后尝试使最后一个元素为null并递增其序列号。)

在上面的论文中更详细地描述了这种结构的正确性,但基本上通过成功递增A[k]元素,你确保在那一刻它仍然是列表中的最后一个元素,并告知通过使他们的初始CAS失败,你“获得下一次射击”的任何同时竞速推进操作。如果第二个CAS成功,则表示您已成功添加元素。

请注意,并发推送和弹出操作可能会导致彼此无限期地失败。推送线程增加A[k-1]和弹出增量A[k-1],然后每个都失败,因为它们看到另一个的增量。冲洗并重复。这是活锁的一个例子。

请注意,如果只有一个线程在运行,问题就会消失,这是阻碍自由的关键观察。

1 ...它也避免了完整版本的dequeue中的“环绕”问题,但我不认为在堆栈的情况下会发生这种情况。

答案 1 :(得分:1)

我不确定是否可以提供简单的示例。简单的东西通常是无等待的(例如RCU的阅读器侧)或无锁(例如CAS重试循环,其中不能使用活锁),或者甚至没有阻塞。

有关无锁多写入器多读取器队列的分析,请参阅Lock-free Progress Guarantees,其中在中间操作中休眠的编写器可以阻塞整个队列,即使它具有高性能/低争用且在实践中很有用。 / p>

我认为只有当线程可以取消其他线程的部分完成操作时,才可以在没有锁定的情况下进行无障碍。 (从而处理在唤醒时取消自己的操作的情况)。我可能弄错了;也许还有其他方法,算法可以是非阻塞但不是无锁。

这不是我会考虑的简单,但是https://en.wikipedia.org/wiki/Non-blocking_algorithm说:

  

一些无障碍算法在数据结构中使用一对“一致性标记”。读取数据结构的过程首先读取一个一致性标记,然后将相关数据读入内部缓冲区,然后读取另一个标记,然后比较标记。如果两个标记相同,则数据是一致的。当读取被更新数据结构的另一个进程中断时,标记可能是不相同的。在这种情况下,进程会丢弃内部缓冲区中的数据并再次尝试。

如果争用的退避算法不好,这可以活锁。由于有太多线程敲击它,它们可能会在任何事情完成之前不断相互抵消。这就是它没有锁定的原因。