不可分割的操作在多处理器和多核系统中是否仍然不可分割?

时间:2010-03-04 22:41:52

标签: multithreading thread-safety multicore multiprocessing

根据标题,加上有哪些限制和陷阱。

例如,在x86处理器上,大多数数据类型的对齐是可选的 - 优化而不是要求。这意味着指针可以存储在未对齐的地址,这反过来意味着指针可能会在缓存页边界上分割。

显然,如果你在任何处理器上努力工作(选择特定的字节等),就可以做到这一点,但不是你仍然期望写操作不可分割。

我非常怀疑多核处理器可以确保其他内核可以保证在这种未对齐写入交叉页面边界情况下的写入指针的前后全部或全部后视图。

我是对的吗?是否有任何类似的陷阱我没有想过?

5 个答案:

答案 0 :(得分:2)

所有线程都可见的单个内存的概念不再适用于具有单独缓存的多个内核。 StackOverflow questions on memory barriers可能会引起关注;比如,this one

我认为用“单一记忆”模型来说明问题的一个例子是: 最初,x = y = 0。

主题1:

X = x;
y = 1;

主题2:

Y = y;
x = 1;

当然,有竞争条件。除了明显的竞争条件之外的第二个问题是一个可能的结果是X = 1,Y = 1。即使没有编译器优化(即使您在汇编中编写上述两个线程)。

答案 1 :(得分:1)

也许我误解了这个例子,但是“未对齐指针”问题 与单核执行时相同。如果数据可以是部分的 写入内存然后不同的线程可以看到部分更新(如果 没有适当的锁定)在任何具有preemtive的机器上 多任务(即使在单CPU系统上)。

除非您正在编写驱动程序,否则不必担心缓存 用于支持DMA的外设。现代多处理器是缓存 连贯,所以硬件保证处理器A上的线程会 与处理器B上的线程具有相同的内存视图。如果是线程 on A读取缓存在B上的内存位置,然后读取A上的线程 将从Bs缓存中获取正确的值。

必须担心寄存器和来自a的值 编程的立场,差异可能不是一个可见的,但 在我看来,经常在并发讨论中涉及缓存 只是引入了不必要的混淆。

编程手册标记为“不可分割”的任何操作 对于ISA,必须合理地保持在多处理中不可分割 使用ISA或向后兼容性的处理器构建的系统 会打破。但是,这并不意味着那些操作 从来没有承诺过不可分割,但碰巧是在一个特定的 处理器的实现,将来是不可分割的 实现(例如在多处理器系统中)。

[编辑]完成以下评论

  1. 写入记忆的任何东西都将被所有人连贯地看到 线程,无论核心数量(在缓存中是否一致) 系统)。
  2. 以非原子方式写入内存的任何内容都可能最终成为部分内容 在存在抢占的情况下由非同步线程读取(甚至 在单核系统上。)
  3. 如果指针被写入单个原子中的未对齐地址 然后写入缓存一致性硬件将确保所有 线程看到它完成了,或者根本没有完成。如果指针被写入 非原子的(例如两个单独的写操作)然后任何 即使在单核系统上,线程也可能看到部分更新 真正的先发制人。

答案 2 :(得分:1)

在x86上,如果汇编操作以锁定指令为前缀,则答案为是,然后处理器断言硬件信号,确保后续指令是原子的(在某些处理器中,缓存协调以确保操作是原子的) 。

使操作成为原子是编译器不能做的事情,在多处理器系统上,原子汇编语言操作非常昂贵,通常用于实现OS / C库提供的锁定原语。

不应将纯粹的高级语言内存操作视为原子操作。如果您有多个线程写入相同的共享内存位置,那么您需要使用一些互斥/锁定机制来避免比赛。

答案 3 :(得分:0)

此类是否可以输出“False”?

class Unsafe
{
  static bool underwearOn, trousersOn;

  static void Main( )
  {
    new Thread(Wait).Start( );    // Start up the busy waiter
    Thread.Sleep(1000);           // Give it a second to start up!

    underwearOn = true;
    trousersOn = true;
  }

  static void Wait(  )
  {
    while (!trousersOn)
        ; // Spin until trousersOn
    Console.Write(underwearOn);
  }
}

是的,在多核计算机上。值类型(例如bool)可以存储在机器寄存器中,并且订单寄存器是同步的,是特定于机器的。可以在underwearOn之前同步trousersOn

您可以锁定分配和while循环,但这会损害性能。更好的解决方案是声明bool变量volatile。这些变量不存储在寄存器中。

编辑:

这是Threading Complete提供的演示文稿的简化示例。

答案 4 :(得分:0)

  

不可分割的行动仍然存在   在多处理器上不可分割   多核系统?

“不可分割”(或“原子”)这一概念在顺序(单核单线程)系统中毫无意义。为了知道某些东西是否不可分割,你需要一个外部观察者,而这个外部观察者只能是另一个线程,无论是在同一个核心上还是在不同核心上。不可分割意味着没有外部观察者可以观察到中间状态。让我推荐“多核编程的艺术”一书,以便更深入地了解这些概念。

你可能会问的是,看似不可分割的操作(例如单行语句x = 3)是否实际上是不可分割的。答案是否定的,并且存在一个众所周知的例子:Java中双打的处理。 double存储在两个32位字中,JVM规范并不保证双精度操作是原子操作(尽管它们在所有主流JVM中都有实际应用)。另一个线程可能会观察到一个状态,其中只更新了两个中的一个字。再一次,这与两个线程是在同一核心上还是在不同核心上进行调度无关。

在任何情况下,只要您希望在一致状态下观察共享数据,就应始终依赖同步事件(例如对volatile变量,障碍,锁等的读取或写入)。避免这些问题的另一个选择是完全避免共享状态。纯功能或消息传递语言可以实现这一点。