小编典典

锁定未锁定的互斥体的效率如何?互斥锁的成本是多少?

all

在低级语言(C、C++ 或其他语言)中:我可以在拥有一堆互斥体(如 pthread 给我的或本机系统库提供的任何东西)或单个对象之间进行选择。

锁定互斥体的效率如何?即可能有多少汇编指令以及它们需要多少时间(在互斥锁被解锁的情况下)?

互斥锁的成本是多少?真的 有很多 互斥锁是个问题吗?或者我可以在我的代码中抛出和我有int变量一样多的互斥变量,这并不重要?

(我不确定不同硬件之间有多少差异。如果有,我也想了解它们。但主要是我对通用硬件感兴趣。)

关键是,通过使用许多互斥锁,每个互斥锁只覆盖对象的一部分,而不是整个对象的单个互斥锁,我可以保护许多块。我想知道我应该走多远。即,我是否应该尽可能地保护任何可能的块,无论这意味着多少更复杂和多少互斥锁?


WebKits blog post (2016) about locking与this question非常相关,并解释了自旋锁、自适应锁、futex等之间的区别。


阅读 121

收藏
2022-07-17

共1个答案

小编典典

我可以在拥有一堆互斥锁或一个对象的单个互斥锁之间进行选择。

如果你有很多线程并且对对象的访问经常发生,那么多个锁会增加并行度。以可维护性为代价,因为更多的锁定意味着更多的锁定调试。

锁定互斥体的效率如何?即可能有多少汇编指令以及它们需要多少时间(在互斥锁被解锁的情况下)?

精确的汇编指令是互斥锁的最小开销——内存/缓存一致性保证是主要开销。并且不太经常使用特定的锁
- 更好。

互斥体由两个主要部分组成(过于简单化):(1)指示互斥体是否被锁定的标志和(2)等待队列。

更改标志只是几条指令,通常无需系统调用即可完成。如果互斥锁被锁定,系统调用将发生将调用线程添加到等待队列并开始等待。如果等待队列为空,则解锁成本很低,但否则需要系统调用来唤醒其中一个等待进程。(在某些系统上,使用便宜/快速的系统调用来实现互斥锁,它们仅在争用的情况下才会变成慢(正常)系统调用。)

锁定未锁定的互斥锁真的很便宜。解锁没有竞争的互斥锁也很便宜。

互斥锁的成本是多少?真的有很多互斥锁是个问题吗?或者我可以在我的代码中抛出与 int 变量一样多的互斥变量,这并不重要?

您可以根据需要将尽可能多的互斥变量放入代码中。您只受到应用程序可以分配的内存量的限制。

概括。用户空间锁(尤其是互斥锁)很便宜,并且不受任何系统限制。但是它们中的太多会成为调试的噩梦。简单表:

  1. 更少的锁意味着更多的争用(缓慢的系统调用、CPU 停顿)和更少的并行度
  2. 更少的锁意味着调试多线程问题的问题更少。
  3. 更多的锁意味着更少的争用和更高的并行度
  4. 更多的锁意味着更多的机会遇到不可调试的死锁。

应该找到并维护一个平衡的应用程序锁定方案,通常平衡#2 和#3。


(*) 较少经常锁定的互斥锁的问题是,如果您的应用程序中的锁定过多,则会导致大部分 CPU/内核间流量从其他 CPU
的数据缓存中刷新互斥锁内存以保证缓存一致性。缓存刷新就像轻量级中断,由 CPU 透明地处理 -
但它们确实引入了所谓的停顿(搜索“停顿”)。

停顿是导致锁定代码运行缓慢的原因,通常没有任何明显的迹象表明应用程序运行缓慢。(有些架构提供 CPU/核心间的流量统计,有些不提供。)

为了避免这个问题,人们通常会使用大量的锁来降低锁竞争的概率并避免停顿。这就是为什么存在不受系统限制的廉价用户空间锁定的原因。

2022-07-17