如果这种类型的哈希表可以定义为无锁?

时间:2013-12-12 09:59:28

标签: concurrency hashmap lock-free

根据我的理解

block is there are =1 task could progress if there are N tasks concurrent running 
and one enter the critical area(enter the critical area one).

lock-free is there are >=1 tasks could progress if there are N tasks concurrent 
running and one enter the critical area.

wait-free is there are N tasks could progress if there are N tasks concurrent 
running and one enter the critical area(maybe it shouldn't be called `critical area`).

我的问题是:

如果哈希表有N个桶,并且每个桶都有一个锁,那么任何时候都应该有> = 1个任务正在进行中。这种类型的哈希表是否可以定义为无锁?

bucket
+-------------+      +-------+      +-------+
| head | lock | ---> | entry | ---> | entry | ---> ...
+-------------+      +-------+      +-------+

2 个答案:

答案 0 :(得分:2)

我对锁定免费的理解是:

有多个任务正在运行并发,并且每个任务都可以访问共享数据,而其中任何一个都可能阻止其他任务的进度。因此,如果您挂起单个线程,它将永远不会阻止其他线程取得进展。

所以(我认为)如果你使用任何锁,你的代码就不是免费的。实际上你实际上使用了锁,因为多个线程可能会尝试并发访问共享数据,因此这个单一的锁可能会被争议和序列化访问;显然,这种潜力意味着您的代码不是无锁的。您在存储桶中使用的锁可能是mutex(操作系统管理队列并在获取锁时提供事件通知)或繁忙循环(CPU在应用程序空间中旋转,试图以原子方式获取锁独家),但都没有锁定。

如果满足以下条件,back-off使用Also if you suspend a single thread, it will never prevent other threads from making progress.的某种技术是免费锁定的。{/ 1}}。

在您的哈希表中,多个线程可以取得进展,但尽管线程可以在操作同一存储桶中的元素时相互阻塞。在存储桶的链接列表中,您可以使用细粒度锁 - 让更多的一个线程与列表中的元素一起使用 - 但即使这样也不是真正的无锁。

修改

免费锁定和免费等待(来自C++ Concurrency in action书籍的定义)之间有所不同:

无锁:对于符合无锁资格的数据结构,必须能够有多个线程 同时访问数据结构。他们不必做同样的事情 操作;一个无锁的队列可能允许一个线程推送,一个线程可以弹出但是 如果两个线程试图同时推送新项目,则中断。不仅如此,如果其中之一 访问数据结构的线程由调度程序中途暂停 通过其操作,其他线程仍然必须能够完成其操作而无需等待挂起的线程。

经常对数据结构使用比较/交换操作的算法 它们中有循环。具有这种循环的无锁算法可能导致一个线程遭受饥饿。如果另一个线程以“错误”时序执行操作,则另一个线程执行操作 线程可能会在第一个线程不断重试其操作时进行。避免此问题的数据结构是等待的,也是无锁的。

等待免费:无等待数据结构是一种具有附加属性的无锁数据结构 访问数据结构的每个线程都可以在有界内完成其操作 步数。算法可以 由于与其他线程的冲突,涉及无限次重试 因此不能等待。

通过这个定义,我认为我的描述就像锁定免费;)。但是对于你的哈希映射,我的投票是基于锁的代码。同样在该书中,作者已经实现了这样的哈希表并将其插入到他的书的lock-based data structures section

答案 1 :(得分:2)

  

如果哈希表有N个桶,并且每个桶都有一个锁,那么任何时候都应该有> = 1个任务正在进行中。这种类型的哈希表是否可以定义为无锁?

没有。 “每个人都有一个锁”意味着可能阻塞。作为“无锁”的测试,请考虑挂起的线程是否可以阻止另一个线程在其暂停的整个时间内取得进展。如果是这样,那么这不是一个无锁的情况。

在无锁编程中,你使用像原子操作这样的东西 - 经常会有更新失败 - 这样一个尝试取得进展的线程可能不得不重试,但每次有一个新的竞争条件,看看它们是否进展;他们真的有机会。

旋转锁

(我在评论中加入了一些细节,但这很笨拙。)

“旋转锁定”对不同的人意味着不同的东西。

从历史上看,有些人称互斥锁或读写锁是自旋锁,如果它至少跨越几(一百/千)次尝试原子地获取锁,然后再加入该锁的操作系统队列(因此​​它赢了'如果持有锁的线程长时间运行,则继续燃烧CPU。

http://msdn.microsoft.com/en-us/library/ms894034.aspx的Microsoft以不同的方式使用它:

  

只有持有自旋锁的线程才能使用该资源,直到释放锁定为止。自旋锁可防止其他线程访问资源。在等待自旋锁释放时,另一个线程可以启动一个尝试获取自旋锁的循环并继续循环,直到持有锁的线程释放自旋锁。

因此,在这种情况下,他们认为自旋锁类似于我上面描述的用法,但没有规定回退到事件驱动的队列以避免浪费CPU时间。

无锁原子操作也倾向于旋转 - 区别在于他们在旋转停止时完成了他们的工作,而不是关闭竞争线程以允许他们开始工作(并且阻止那些竞争线程)即使锁定线程以某种方式被暂停或延迟,也可以从进度中获得进展。)