我了解什么是数据竞争,以及锁/互斥/信号灯如何防止它们的基础知识。但是,如果锁本身具有“竞赛条件”,会发生什么?例如,可能在同一应用程序中但在不同处理器上运行的两个不同线程尝试在完全相同的时间获取锁。
那会发生什么呢?为防止这种情况该怎么做?这是不可能的,还是完全不可能?还是等待发生的真实比赛条件?
我了解什么是数据竞争,以及锁/互斥/信号灯如何防止它们的基础知识。但是,如果锁本身具有“竞赛条件”,会发生什么?例如,可能在同一应用程序中但在不同处理器上运行的两个不同线程尝试在完全相同的时间获取锁。
那会发生什么呢?为防止这种情况该怎么做?这是不可能的,还是完全不可能?还是等待发生的真实比赛条件?
Answers:
这是不可能的,还是完全不可能?
不可能。它可以通过不同的方式来实现,例如,通过比较和交换来实现,其中硬件保证了顺序执行。在存在多个内核甚至多个套接字的情况下,它可能会变得有些复杂,并且在内核之间需要复杂的协议,但这已得到解决。
研究原子“测试和设置”操作的概念。
本质上,该操作无法划分-不可能同时执行两件事。它会检查一个值,将其设置为清晰,然后返回测试时的值。在锁定操作中,经过测试设置后,结果始终为“ lock == TRUE”,唯一的区别是开始时是否设置。
在单核处理器的微代码级别,这是一条不可分割的指令,并且易于实现。对于多核和多核处理器而言,它变得越来越难,但是作为程序员,我们无需担心它,因为它是为从事硅技术的真正聪明的人设计的。从本质上讲,它们执行相同的操作-发出原子指令,即测试和设置的理想版本
两个(或更多)线程不可能同时获得锁。例如,有几种类型的同步方法:
伪代码:
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)-这并不总是事实。当线程被挂起时,称为上下文切换的昂贵操作就发生了。但是,在等待时间短(线程数〜内核数)时,这是合理的。