"锁是一项昂贵的操作"经常被说出来的原因是什么?

Ton*_*ion 12 performance multithreading synchronization

我已经阅读了很多关于线程的资料,以及所涉及的所有同步机制.我也理解不做正确的危险.

我刚观看了这篇关于Parallelism和Concurrency的PDC 2009视频,这里再次提到"锁是一项昂贵的操作".我现在在各种文本,书籍中遇到过这样的短语,我听说该领域的专家也说过.

我想知道,获得锁(互斥锁或信号量)究竟有多么昂贵?这是因为它导致LOCK#指令在汇编程序级别发出吗?

获取锁需要内核调用进入操作系统吗?

为什么锁被认为是昂贵的操作?"昂贵"是一个相当相对的术语,所以如果我们比较创建一个新线程(需要设置一个线程堆栈等),真正获得一个锁是多么昂贵?

盖子下面发生了什么?

我的猜测是,它不可能那么昂贵,因为我确信Windows(例如)可以运行,必须始终使用数百个锁/同步机制.

谁能详细说明?

注意:我只是好奇,我知道线程是如何工作的,我也不打算做一些愚蠢的优化.

kfm*_*e04 9

排比

当可变共享数据需要锁定时,您可能会失去并行性的好处.

让我们首先简化上下文切换是免费的并且锁是便宜的(两者都不是完全正确的 - 我们将在最后解决这些问题).

  • 想想没有数据的线程的情况:线程可以独立运行 而不必担心其他线程的状态.拥有两个线程将使您的算法运行速度提高两倍.

  • 接下来,介绍一些随时间变化的共享数据.根据定义,没有两个线程可以同时修改/读取此数据.这意味着如果两个线程碰巧想要访问此数据,则不再需要并发操作:它们必须以序列化(同步)​​方式工作.这种争用发生的频率越高,应用程序的行为就像单线程应用程序一样,而不是双线程应用程序.

因此,当说"锁是一项昂贵的操作"时,我认为它是由于可能失去并行性而不是锁本身是昂贵的.

成本

除了并行性的损失之外,如果你积累了锁定和潜在同步和上下文切换的小但非零成本,那么拥有锁定很可能会降低算法速度.

另请注意,您尝试同时访问该锁的线程越多,您的算法就会越多地串行运行而不是并行运行.操作系统还必须花费更多的周期来通过锁定产生的小吸管来处理所有情况.

减轻

另一方面,可以通过不经常调用它们来减轻锁定的缺点,而不是颠簸(锁定/解锁一次而不是在紧密块内多次锁定/解锁),或者通过使用管道或消费者/生产者模式(用条件变量发信号).

无锁操作的一个技巧包括在产生任何线程之前执行所有共享数据初始化,并且仅在产生后读取该数据.

术语

最后一条评论:需要锁来避免共享资源上的竞争条件. 用只是拥有锁的结果 - 它只是意味着一个线程可以阻塞/等待另一个线程锁定的锁.实际上发生争用的频率取决于很多因素:线程数与核心数,锁中花费的时间,执行的运气(取决于调度算法),运行期间操作系统的状态等等......


Dav*_*rtz 6

它是否会导致在汇编器级别发出 LOCK# 指令?

不,因为它并不总是这样做。

获取锁是否需要内核调用操作系统?

不,因为它通常不会这样做。

事实上,锁非常非常便宜。争论的代价是昂贵的。如果必须在锁定和争用之间做出选择,大多数时候锁定是更好的选择。

正确使用锁可以避免争争用的机制。它们会自动找到竞争的线程并对其进行取消调度,这样一来,最终主要是不竞争的线程同时运行。

例如:假设您有四个准备运行的线程:ABCD。假设AB相互竞争(假设他们操作同一个集合)。并假设CD相互竞争,但A不与C竞争。如果AB同时运行(竞争),锁将导致其中一个线程未准备好运行,调度程序将调度C(或D),并且两个线程将运行而不会进一步竞争。(至少直到下一次上下文切换为止。)

通常,当人们说“锁很昂贵”时,他们的意思是争用很昂贵。不幸的是,通过他们的措辞方式,他们经常鼓励人们尽量减少锁定,但会增加过程中的争用。在绝大多数情况下,这是一个失败的主张。(也有一些例外。)