【问题标题】:Multithreading and out-of-order execution多线程和乱序执行
【发布时间】:2011-12-21 14:08:00
【问题描述】:

假设我们有一个多线程 C 程序 (pthreads),并且各个线程的(非同步的)共享变量访问不会被编译器重新排序。 x86 CPU 是否尊重共享变量访问的顺序(在单个线程内),或者它是否可能重新排序一些内存访问?

【问题讨论】:

  • 乱序/推测调度执行只存在于微码级别(后端)。 (请通知@我,因为我不会手动检查答案)

标签: multithreading x86 shared-memory


【解决方案1】:

不同步的共享变量访问是危险的,乱序是其原因之一。
x86 保持写入顺序(在一个线程内),但不读取。

如果您认为订单仍然存在,这可能会给您带来麻烦。例如:
线程 A 写入 x,然后写入 y。假设编译器没有重新排序,cpu 不会重新排序(x86 不会,其他可能)。
线程 B 读取 y,然后读取 x。您可能会认为,如果它获得了 y 的新值,那么您肯定也会获得 x 的新值。
不是这样。 CPU 可能会重新排序线程 B 的读取,因此实际上会更早地读取 y。

编辑:正如“Man of One Way”所指出的,在这种情况下,x86(但不是所有处理器!)保证排序。
我引用英特尔软件开发者手册:

单个处理器的写入被所有人以相同的顺序观察 处理器。

这不适用于多个处理器的写入 - 不同处理器的写入顺序似乎不同。

但是,我强烈建议不要依赖它,而是使用适当的同步。
同步原语是通过原子操作和/或屏障实现的,可确保您的安全。

【讨论】:

  • 您是说可以重新排序一个阅读与另一个阅读? “线程 B 读取 y,然后读取 x。你可能会认为,如果它得到 y 的新值,那么你肯定也会得到 x 的新值。”我不认为这是真的根据英特尔手册:“读取不会与其他读取重新排序”。不过,可以使用较旧的写入重新排序读取。
  • @ManofOneWay,你是对的。该手册还说“所有处理器以相同的顺序观察单个处理器的写入”。我认为我的底线仍然是正确的——根据这些细节编写正确的无同步代码几乎是不可能的。
  • x86 不保证加载和旧存储的顺序,因此如果有一个存储 A,然后是一个加载 B,并且它们引用不同的内存位置,则可以移动加载 B在 A 之前。所以在某些情况下仍然需要围栏。
  • 一个小问题,这可能很重要:x86 保证写入到相同的内存位置之间的顺序。
【解决方案2】:

由于存储缓冲区的存在,一些重新排序是可能的。参见例如https://www.cl.cam.ac.uk/~pes20/weakmemory/cacm.pdf

但是,只有几个线程才能看到重新排序,在单个线程中,来自该线程的所有访问似乎都是按顺序发生的。

【讨论】:

    猜你喜欢
    • 2016-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-12
    相关资源
    最近更新 更多