1

我记得在我上大学的一门课程中,我最喜欢的竞争条件示例之一是一个简单的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 上同时运行?

4

3 回答 3

1

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

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

基本上,这一切都变得非常棘手 - 这就是为什么在访问共享数据时使用同步使用由真正知道自己在做什么的专家编写的无锁高级抽象通常是一个好主意的原因。

于 2011-04-03T17:40:00.027 回答
1

是的,双核 CPU 的引入使大量具有潜在线程竞争的程序迅速失败。单核 CPU 多任务由调度程序快速切换线程之间的线程上下文。这消除了与陈旧 CPU 缓存相关的一类线程错误。

但是,您给出的示例也可能在单个内核上失败。当线程调度程序在将变量的值加载到寄存器中以增加它时中断线程。它不会那么频繁地失败,因为调度程序中断线程的可能性并不大。

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

于 2011-04-03T17:53:06.037 回答
1

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

其次,实际上只有两种情况真正有趣:单线程执行,以及其他所有情况。一旦您拥有多个线程(即使所有线程都在单个处理器上运行),您就会遇到与在具有数千个处理器的大型机器上运行相同的潜在问题。现在,当代码在更多处理器上运行(最多与您创建的线程一样多)时,您很可能会更快地看到问题出现,但问题本身没有/没有完全改变。

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

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

于 2011-04-03T17:59:52.607 回答