在什么情况下,无锁数据结构比基于锁的数据结构更快?

时间:2016-10-27 13:45:45

标签: c++ multithreading data-structures concurrency lock-free

我目前正在阅读 Anthony Williams 的书C++ Concurrency in Action,并且有几个无锁数据结构实现。在安东尼写的书中关于无锁数据结构的章节的前面是:

  

这给我们带来了无锁和无等待代码的另一个缺点:虽然它可以增加数据结构上操作并发的可能性并减少单个线程等待的时间,但它可能会降低整体性能。

事实上,我测试了本书中描述的所有无锁堆栈实现,而不是前面章节中的基于锁的实现。似乎无锁代码的性能总是低于基于锁的堆栈。

在什么情况下,无锁数据结构更优化,必须首选?

5 个答案:

答案 0 :(得分:4)

无锁结构的一个好处是它们不需要上下文切换。但是,在现代系统中,无竞争锁也可以无上下文切换。为了从无锁算中获益(性能方面),必须满足几个条件:

  • 争论必须很高
  • 应该有足够的CPU核心,以便旋转线程可以不间断地运行(理想情况下,应固定在自己的核心)

答案 1 :(得分:3)

几年前我做过表演研究。当线程数量很小时,无锁数据结构和基于锁的数据结构是可比较的。但随着线程数量的增加,在某些时候,基于锁的数据结构会出现明显的性能下降,而无锁数据结构可以扩展到数千个线程。

答案 2 :(得分:1)

互斥设计很少,如果没有执行无锁设计。 所以后续问题是为什么有人会使用互斥锁而不是无锁设计?

问题在于无锁设计很难做到,并且需要大量设计才能可靠;虽然互斥是相当简单的(相比之下),并且调试时可能更难。出于这个原因,人们通常更喜欢先使用互斥锁,然后在争用被证明是瓶颈之后再迁移到锁定状态。

答案 3 :(得分:1)

取决于碰撞的概率。

如果碰撞很可能,互斥是最佳解决方案。 例如:2个线程不断将数据推送到容器的末尾。 使用锁定自由只有1个线程会成功。另一个需要重试。在这种情况下,阻止和等待会更好。

但是如果你有一个大容器,并且2个线程将访问不同区域的容器,很可能会发生冲突。 例如:一个线程修改容器的第一个元素,另一个线程修改最后一个元素。 在这种情况下,重试的概率非常小,因此锁定自由度会更好。

锁定自由的其他问题是自旋锁(大量内存使用),原子变量的整体性能以及对变量的一些约束。

例如,如果您的约束x == y需要为true,则不能对x和y使用原子变量,因为您不能同时更改这两个变量,而lock()将满足约束

答案 4 :(得分:1)

我认为这些答案中缺少的一件事是锁定期。如果你的锁定周期很短,即在获取锁之后,如果你执行一个任务很短的时间(比如增加一个变量),那么使用基于锁的数据结构会带来不必要的上下文切换、cpu 调度等。在这种情况下,无锁是一个不错的选择,因为线程会旋转很短的时间。