Answers:
本质上,线程争用是一个线程正在等待另一线程当前持有的锁/对象的情况。因此,该等待线程无法使用该对象,直到另一个线程已解锁该特定对象。
几个答案似乎集中在锁争用上,但是锁并不是唯一可以在其上经历争用的资源。争用仅是当两个线程试图以这样一种方式访问同一资源或相关资源时,至少一个竞争线程的运行速度比其他线程未运行时的运行速度慢。
最明显的争用示例是锁定。如果线程A具有锁,而线程B要获取该锁,则线程B将不得不等待,直到线程A释放该锁。
现在,这是特定于平台的,但是即使不必等待另一个线程释放锁,该线程也可能会变慢!这是因为锁可以保护某种数据,并且数据本身也经常会被争用。
例如,考虑一个获取锁,修改对象,然后释放锁并执行其他操作的线程。如果有两个线程正在执行此操作,即使它们从未争夺锁,则这些线程的运行速度可能比仅运行一个线程时要慢得多。
为什么?假设每个线程都在现代x86 CPU上的独立内核上运行,并且这些内核不共享L2缓存。仅使用一个线程,该对象可能大部分时间都保留在L2缓存中。在两个线程都运行的情况下,每次一个线程修改对象时,另一个线程都会发现数据不在其L2高速缓存中,因为另一个CPU使高速缓存行无效。例如,在奔腾D上,这将导致代码以FSB速度运行,这远低于L2缓存速度。
由于即使锁本身不存在争用也会发生争用,因此在没有锁的情况下也会发生争用。例如,假设您的CPU支持32位变量的原子增量。如果一个线程不断增加和减少变量,则该变量将在大部分时间处于高速缓存中。如果有两个线程这样做,它们的高速缓存将争夺拥有该变量的内存的所有权,并且由于高速缓存一致性协议用于保护高速缓存行的每个核心所有权,因此许多访问将变慢。
具有讽刺意味的是,锁通常会减少争用。为什么?因为没有锁,所以两个线程可以在同一个对象或集合上操作并引起大量争用(例如,有无锁队列)。锁将趋向于调度竞争线程,从而允许非竞争线程运行。如果线程A持有一个锁,而线程B想要相同的锁,则实现可以改为运行线程C。如果线程C不需要该锁,则可以暂时避免线程A和B之间的将来争用。(当然,这是假定还有其他线程可以运行。如果整个系统唯一可以取得有益进展的方法是运行竞争的线程,则无济于事。)
从这里:
当线程正在等待不容易获得的资源时,就会发生争用。它会减慢代码的执行速度,但会随着时间的流逝而清除。
当线程正在等待第二个线程已锁定的资源,而第二个线程正在等待第一个线程已锁定的资源时,就会发生死锁。死锁可能涉及两个以上的线程。僵局永远无法解决。它通常会导致整个应用程序或遇到死锁的部分停止运行。
我认为OP应该对问题的背景作一些说明-我可以想到2个答案(尽管我确定此列表中还有其他内容):
如果您指的是线程争用的一般“概念”及其在应用程序中的呈现方式,请参考上面的@DavidSchwartz的详细解答。
还有“ .NET CLR锁和线程:争用总数”性能计数器。从此计数器的PerfMon描述中得出,其定义为:
此计数器显示CLR中的线程尝试不成功获取托管锁的总次数。可以通过多种方式获取托管锁。通过C#中的“锁定”语句或通过调用System.Monitor.Enter或使用MethodImplOptions.Synchronized自定义属性来实现。
...而且我敢肯定其他操作系统和应用程序框架也适用。
当线程尝试获取对其他线程已经获取的对象的锁时,就会发生锁争用*。在释放对象之前,线程将被阻塞(换言之,它处于等待状态)。在某些情况下,这可能会导致所谓的串行执行,从而对应用程序产生负面影响。