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

时间:2012-01-25 23:44:06

标签: multithreading performance synchronization

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

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

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

获取锁是否需要对OS进行内核调用?

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

封面下发生了什么?

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

任何人都可以详细说明吗?

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

5 个答案:

答案 0 :(得分:9)

并行

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

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

  • 考虑不共享数据的线程的情况:线程可以运行 独立而不用担心其他线程的状态。拥有两个线程将使您的算法运行速度提高两倍。

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

因此,当说“锁是一项昂贵的操作”时,我认为这是因为可能会失去并行性,而不是锁本身很昂贵。

费用

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

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

缓解措施

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

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

术语

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

答案 1 :(得分:4)

锁是昂贵的,因为它们阻止其他线程获得锁。延迟可能是致命的,使得多处理器系统比没有锁的单线程设计慢。

答案 2 :(得分:4)

  

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

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

  

获取锁是否需要对OS进行内核调用?

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

事实上,锁非常非常便宜。这是争用,这是昂贵的。如果您必须在锁定和争用之间进行选择,那么大多数情况下锁定是更好的选择。

如果使用得当,锁定是一种争用避免机制。它们会自动查找竞争和取消调度它们的线程,以便主要使用不会同时竞争的线程结束。

例如:假设您有四个可以运行的线程, A B C D < / em>的。说 A B 相互竞争(比如他们操纵同一个集合)。并说 C D 相互竞争,但 A 不与 C 竞争。如果 A B 同时运行(竞争),则锁将导致其中一个未准备好运行,然后调度程序将安排 C (或 D ),两个线程将在没有进一步争用的情况下运行。 (至少在下一次上下文切换之前。)

通常,当人们说“锁很贵”时,他们就认为争用很昂贵。不幸的是,通过按照他们的方式来表达,他们经常鼓励人们减少锁定,但会增加过程中的争用。在绝大多数情况下,这是一个失败的主张。 (有一些例外。)

答案 3 :(得分:1)

简短的回答是:不,它们并不昂贵。

更长的答案是:是的,它们很昂贵,因为除非您的锁无意义并且什么也不做,否则锁的存在会降低您的代码速度。

实际答案需要澄清:

技术与实施与设计:

技术 David Schwartz回答澄清technically the locks do not slow down code.也就是说,如果您运行带有锁定语句的单线程代码,锁定不会让您失望。*

实施: Mark Ransom在他的answer中指出锁定速度很慢,因为这些阻止导致应用速度变慢的延迟。

设计:真正的答案是包含锁的设计往往比没有设计的设计慢。锁定设计以减慢您的代码。

所以,如果你能够合理地设法编写锁从未被命中或甚至不需要的代码,你绝对应该这样做。这就是人们说“锁很贵”的原因。并不是lock(obj)语句是昂贵的,而是阻止其他线程的行为是昂贵的,并且只应在您评估它值得花费的情况下进行。

* 当然,如果您在单线程应用程序中获得锁争用,它将永远不会完成。

答案 4 :(得分:0)

锁定通常是自旋锁,这会导致线程“旋转”,在执行锁定时不会执行任何有用的工作。

无所事事是昂贵的,因为众所周知:“时间就是金钱”。