双CPU机器上的线程协作

时间:2011-04-03 17:29:00

标签: multithreading race-condition context-switch

我记得在我上大学的一门课程中,我最喜欢的竞争条件之一就是一个简单的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--;
}

教授随后询问i在经过数十亿亿次运行之后的价值。 (如果它本身就是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上同时运行?

3 个答案:

答案 0 :(得分:1)

你仍然有竞争条件 - 它根本不会改变。想象两个核心同时执行增量 - 它们都加载相同的值,增加到相同的值,然后存储相同的值...所以两个操作的总增量将是一个而不是两个

在内存模型方面存在潜在问题的其他原因 - 步骤1可能真正 检索i的最新值,以及步骤3可能不会立即以其他线程可以看到的方式写入i的新值。

基本上,这一切都变得非常棘手 - 这就是为什么在访问共享数据以使用无锁更高级别抽象时 使用同步通常是个好主意这是由真正了解他们正在做什么的专家撰写的。

答案 1 :(得分:1)

是的,双核CPU的推出使得潜在线程竞赛的大量程序迅速失败。调度程序的单核CPU多任务快速切换线程之间的线程上下文。这消除了一种与过时的CPU缓存相关联的线程错误。

您提供的示例也可能在单个核心上失败。当线程调度程序中断线程时,就像它在寄存器中加载变量的值一样,以便递增它。它几乎不会经常失败,因为调度程序中断线程的可能性并不大。

有一个操作系统功能,允许这些程序无论如何都会跛行而不是在几分钟内崩溃。称为“处理器关联”,可用作Windows上start.exe的AFFINITY命令行选项,winapi中的SetProcessAfinityMask()。查看Interlocked类以获取原子递增和递减变量的辅助方法。

答案 2 :(得分:1)

首先,双核处理器与双核处理器没有实际效果。双核处理器在芯片上仍然有两个完全独立的处理器。它们可能共享一些缓存,并且共享一条到内存/外设的公共总线,但处理器本身是完全独立的。 (双线程单代码,如超线程)是第三种变体 - 但它也有一组每个虚拟处理器的寄存器。这两个处理器共享一组执行资源,但它们保留完全独立的寄存器集。

其次,实际上只有两个案例非常有趣:一个执行线程,以及其他所有内容。一旦你拥有多个线程(即使所有线程都在一个处理器上运行),你就会遇到同样的潜在问题,就好像你在拥有数千个处理器的大型机器上运行一样。现在,当代码在更多处理器上运行时(你创建的线程数量达到最多),你很可能会很快看到问题出现得很多,但问题本身没有/没有完全改变。

从实际的角度来看,从测试的角度来看,拥有更多核心是有用的。考虑到典型操作系统上任务切换的粒度,编写将运行的代码非常容易,而不会在单个处理器上出现问题,这会在几小时甚至几分钟内崩溃和烧毁当你在另外两个或物理处理器上运行它时。问题并没有真正改变 - 当你有更多的处理器时,它更有可能更快地显示出来。

最终,竞争条件(或死锁,活锁等)是关于代码的设计,关于它运行的硬件。硬件可以改变您需要采取的步骤来强制执行所涉及的条件,但相关的差异与简单的处理器数量几乎没有关系。更确切地说,当你不是简单地拥有一台具有多个处理器的机器,而是拥有完全独立的地址空间的多台机器时,它们就会做出让步,所以你可能需要采取额外的步骤来确保当你向内存写一个值时对于无法直接看到内存的其他计算机上的CPU,它变得可见。