是什么阻止了锁上的竞争状况?


24

我了解什么是数据竞争,以及锁/互斥/信号灯如何防止它们的基础知识。但是,如果锁本身具有“竞赛条件”,会发生什么?例如,可能在同一应用程序中但在不同处理器上运行的两个不同线程尝试在完全相同的时间获取锁。

那会发生什么呢?为防止这种情况该怎么做?这是不可能的,还是完全不可能?还是等待发生的真实比赛条件?


这个问题是在此之前问:stackoverflow.com/questions/980521/...
布朗博士

和一个相关的问题在这里P.SE:programmers.stackexchange.com/questions/228827/...
棘轮怪胎

您需要获取一个锁才能获取该锁;)(换句话说,如果您的锁具有竞争条件,则无法正确实施-锁被定义为实现互斥的构造)
Tangrs

您错过了锁如何工作的重要要点。它们被构造成不可能在锁上有座圈,否则它们是完全无用的。
赞恩(Zane)2014年

Answers:


36

这是不可能的,还是完全不可能?

不可能。它可以通过不同的方式来实现,例如,通过比较和交换来实现,其中硬件保证了顺序执行。在存在多个内核甚至多个套接字的情况下,它可能会变得有些复杂,并且在内核之间需要复杂的协议,但这已得到解决。


3
谢谢...天哪...它是在硬件中处理的(或至少比我们触摸的水平低。)
corsiKa 2014年

2
@gdhoward我简直不敢相信...这个答案花了我不到5分钟的时间,这是我的四百个答案(主要是SO)中投票率第三高的。而且可能也是最短的一种。
maaartinus 2014年

1
@maaartinus-有时又简短又可爱。
Bobson 2014年

17

研究原子“测试和设置”操作的概念。

本质上,该操作无法划分-不可能同时执行两件事。它会检查一个值,将其设置为清晰,然后返回测试时的值。在锁定操作中,经过测试设置后,结果始终为“ lock == TRUE”,唯一的区别是开始时是否设置。

在单核处理器的微代码级别,这是一条不可分割的指令,并且易于实现。对于多核和多核处理器而言,它变得越来越难,但是作为程序员,我们无需担心它,因为它是为从事硅技术的真正聪明的人设计的。从本质上讲,它们执行相同的操作-发出原子指令,即测试和设置的理想版本


2
基本上,如果硬件在某种程度上不是本质上是顺序的,它将具有一种机制,可以打破可能发生的联系。
比尔·米歇尔2014年

@BillMichell,我应该想到这一点。实际上,我做到了;我只是不知道我的假设是否正确。
加文·霍华德

2

只需将代码放入专门设计的关键部分,以使竞争条件不会违反互斥。

大多数情况下,使用原子比较和设置循环在硬件级别执行

while(!CompareAndSet(&lock, false, true));//busy loop won't continue until THIS thread has set the lock to true
//critical section
CompareAndSet(&lock, true, false);

在没有这种情况的情况下,已经进行了充分研究的软件解决方案以实现互斥。


1

两个(或更多)线程不可能同时获得锁。例如,有几种类型的同步方法:

主动等待-自旋锁

伪代码:

1. while ( xchg(lock, 1) == 1); - entry protocole

XCHG是原子操作(存在于x86体系结构上)的一个示例,该操作首先为“锁”变量设置新值,然后返回旧值。原子表示不能中断-在上面的示例中,设置新值和返回旧值之间。原子-确定结果,无论如何。

2. Your code
3. lock = 0; - exit protocol

当锁等于0时,另一个线程可以进入关键部分-循环结束。

线程挂起-例如计数信号量

存在两个原子操作.Wait().Signal()我们有一个整数变量让我们称之为它int currentValue

Wait():
if (currentValue > 0) currentValue -= 1;
else suspend current thread;

Signal():
If there exists thread suspended by semaphore wake up one of them
Else currentValue += 1;

现在解决关键部分的问题真的很容易:

伪代码:

mySemaphore.Wait();
do some operations - critical section
mySemaphore.Signal();

通常,您的编程线程API应该使您能够在信号关键部分指定最大并发线程。显然,多线程系统中有更多类型的同步(互斥量,监视器,二进制信号量等),但是它们基于上述思想。有人可能会争辩说,使用线程挂起的方法应该比主动等待更可取(这样就不会浪费CPU)-这并不总是事实。当线程被挂起时,称为上下文切换的昂贵操作就发生了。但是,在等待时间短(线程数〜内核数)时,这是合理的。

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.