我记得在我上大学的一门类(class)中,我最喜欢的竞态条件示例之一是其中一个简单的main()
方法启动了两个线程,其中一个线程将共享(全局)变量增加了一个,另一个减少了它。伪代码:
static int i = 10;
main() {
new Thread(thread_run1).start();
new Thread(thread_run2).start();
waitForThreads();
print("The value of i: " + i);
}
thread_run1 {
i++;
}
thread_run2 {
i--;
}
然后,教授问了10亿亿兆亿次运行后
i
的值(value)是什么。 (如果基本上不是10,则为零。)不熟悉多线程系统的学生回答100%的时间,print()
语句始终将i
报告为10。这实际上是不正确的,因为我们的教授证明了每个增量/减量语句实际上都是(汇编为)3个语句:
1: move value of 'i' into register x
2: add 1 to value in register x
3: move value of register x into 'i'
因此,
i
的值可以是9、10或11。(我不再赘述)。我的问题:
我的理解是,物理寄存器集是特定于处理器的。在使用双CPU机器时(请注意双核和双CPU之间的区别),每个CPU是否都有自己的一组物理寄存器?我以为答案是肯定的。
在单CPU(多线程)计算机上,上下文切换允许每个线程拥有自己的虚拟寄存器集。由于双CPU机器上有两个物理寄存器集,因此这不可能导致更多的竞争情况,因为您实际上可以使两个线程同时运行,而不是在单个虚拟机上同时进行“虚拟”同步运行。 CPU机? (引用每个上下文开关保存/恢复寄存器状态的事实,进行虚拟同时操作。)
更具体地说-如果您是在8 CPU的计算机上运行此程序,那么每个CPU都有一个线程,是否消除了竞争条件?如果将此示例扩展为使用8个线程,则在双CPU机器(每个CPU具有4个内核)上,竞争条件的可能性会增加还是减少?操作系统如何防止汇编指令的
step 3
在两个不同的CPU上同时运行?
最佳答案
是的,双核CPU的引入使大量具有潜在线程争用的程序迅速失败。通过调度程序,单核CPU可以执行多任务,从而在线程之间快速切换线程上下文。这消除了与过时的CPU缓存相关的一类线程错误。
您给出的示例也可能在单个内核上失败。当线程调度程序中断线程时,就像线程将变量的值加载到寄存器中一样,以使其递增。只是不会几乎不那么失败,因为调度程序中断线程的几率并不大。
有一个操作系统功能,可以使这些程序始终运行,而不会在几分钟之内崩溃。称为“处理器相似性”,可作为Windows上start.exe的AFFINITY命令行选项,winapi中的SetProcessAfinityMask()使用。查看Interlocked类以获取自动增加和减少变量的辅助方法。
关于multithreading - 双CPU机器上的线程合作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5531214/