什么是线程争用?


119

有人可以简单解释一下线程争用吗?

我已经用谷歌搜索了,但是似乎找不到简单的解释。


9
因此,写下您对此含糊其词的想法,以便我们可以看到您可能离开的地方,或者您的理解可能是正确的。
詹姆斯·布莱克2009年

Answers:


87

本质上,线程争用是一个线程正在等待另一线程当前持有的锁/对象的情况。因此,该等待线程无法使用该对象,直到另一个线程已解锁该特定对象。


53
这个答案是不完整的(与其他大多数答案一样)。锁是可以争用的一种类型,但远非唯一的一种。无锁资源也可能存在争用。(例如,如果两个线程以原子方式连续递增同一整数,则由于高速缓存ping-ponging,它们可能会发生争用。不涉及锁定。)
David Schwartz

在诸如CPython的全局解释器锁定(GIL)的情况下,线程必须始终获取GIL,因此默认情况下争用在同一进程中运行的多个线程。
Acumenus

我认为您已经按照死锁进行了解释,但这与死锁有很大不同。
Harshit Gupta 2015年

184

几个答案似乎集中在锁争用上,但是锁并不是唯一可以在其上经历争用的资源。争用仅是当两个线程试图以这样一种方式访问​​同一资源或相关资源时,至少一个竞争线程的运行速度比其他线程未运行时的运行速度慢。

最明显的争用示例是锁定。如果线程A具有锁,而线程B要获取该锁,则线程B将不得不等待,直到线程A释放该锁。

现在,这是特定于平台的,但是即使不必等待另一个线程释放锁,该线程也可能会变慢!这是因为锁可以保护某种数据,并且数据本身也经常会被争用。

例如,考虑一个获取锁,修改对象,然后释放锁并执行其他操作的线程。如果有两个线程正在执行此操作,即使它们从未争夺锁,则这些线程的运行速度可能比仅运行一个线程时要慢得多。

为什么?假设每个线程都在现代x86 CPU上的独立内核上运行,并且这些内核不共享L2缓存。仅使用一个线程,该对象可能大部分时间都保留在L2缓存中。在两个线程都运行的情况下,每次一个线程修改对象时,另一个线程都会发现数据不在其L2高速缓存中,因为另一个CPU使高速缓存行无效。例如,在奔腾D上,这将导致代码以FSB速度运行,这远低于L2缓存速度。

由于即使锁本身不存在争用也会发生争用,因此在没有锁的情况下也会发生争用。例如,假设您的CPU支持32位变量的原子增量。如果一个线程不断增加和减少变量,则该变量将在大部分时间处于高速缓存中。如果有两个线程这样做,它们的高速缓存将争夺拥有该变量的内存的所有权,并且由于高速缓存一致性协议用于保护高速缓存行的每个核心所有权,因此许多访问将变慢。

具有讽刺意味的是,锁通常会减少争用。为什么?因为没有锁,所以两个线程可以在同一个对象或集合上操作并引起大量争用(例如,有无锁队列)。锁将趋向于调度竞争线程,从而允许非竞争线程运行。如果线程A持有一个锁,而线程B想要相同的锁,则实现可以改为运行线程C。如果线程C不需要该锁,则可以暂时避免线程A和B之间的将来争用。(当然,这是假定还有其他线程可以运行。如果整个系统唯一可以取得有益进展的方法是运行竞争的线程,则无济于事。)


4
+1同样,为了明确起见,两个内核所争夺的两个变量甚至不必是同一变量即可引起竞争,它们只需存储在同一高速缓存行中的内存中即可。填充结构和/或将结构与内存对齐可以帮助避免这种形式的争用。
Rob_before_edits 2011年

1
@David,请帮助您更详细地了解答案的最后一部分
学习者

4
@Naroji提出一个问题。
大卫·史瓦兹

@DavidSchwartz,您是C程序员吗?
起搏器

@Pacerier C ++主要是。
David Schwartz 2015年

19

这里

当线程正在等待不容易获得的资源时,就会发生争用。它会减慢代码的执行速度,但会随着时间的流逝而清除。

当线程正在等待第二个线程已锁定的资源,而第二个线程正在等待第一个线程已锁定的资源时,就会发生死锁。死锁可能涉及两个以上的线程。僵局永远无法解决。它通常会导致整个应用程序或遇到死锁的部分停止运行。


这也解释了线程争用和死锁之间的区别
Sankalp,2016年

3

我认为OP应该对问题的背景作一些说明-我可以想到2个答案(尽管我确定此列表中还有其他内容):

  1. 如果您指的是线程争用的一般“概念”及其在应用程序中的呈现方式,请参考上面的@DavidSchwartz的详细解答。

  2. 还有“ .NET CLR锁和线程:争用总数”性能计数器。从此计数器的PerfMon描述中得出,其定义为:

    此计数器显示CLR中的线程尝试不成功获取托管锁的总次数。可以通过多种方式获取托管锁。通过C#中的“锁定”语句或通过调用System.Monitor.Enter或使用MethodImplOptions.Synchronized自定义属性来实现。

...而且我敢肯定其他操作系统和应用程序框架也适用。


2

您有2个线程。线程A和线程B,您也有对象C。

A当前正在访问对象C,并且已对该对象设置了锁定。B需要访问对象C,但是只有在A释放对对象C的锁定后才能访问。



1

对我而言,争用是共享资源上两个或多个线程之间的竞争。资源可以是锁,柜台等。竞争是指“谁先获取资源”。线程越多,争用就越多。对资源的访问越频繁,争用就越多。


1

想象以下情况。您正在为明天的期末考试做准备,感到有点饿。所以,你给你弟弟十美元,请他为你买一个比萨。在这种情况下,您是主线程,兄弟是子线程。一旦下达订单,您和您的兄弟就会同时完成工作(即学习和购买比萨饼)。现在,我们有两种情况要考虑。首先,您的兄弟将您的披萨带回来,并在您学习时终止。在这种情况下,您可以停止学习并享用披萨。其次,您要早点完成学习并在披萨可用之前入睡(即,您今天分配的工作-为明天的期末考试做准备)。当然,你不能入睡。否则,您将没有机会吃披萨。

如示例中所示,这两种情况具有竞争的意义。


0

线程争用也会受到I / O操作的影响。线程等待文件读取时的示例可以认为是争用。使用I / O完成端口作为解决方案。


0

当线程尝试获取对其他线程已经获取的对象的锁时,就会发生锁争用*。在释放对象之前,线程将被阻塞(换言之,它处于等待状态)。在某些情况下,这可能会导致所谓的串行执行,从而对应用程序产生负面影响。

dotTrace文档中

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.