【问题标题】:Trying to understand the relation between Thread.MemoryBarrier() and context switching试图理解 Thread.MemoryBarrier() 和上下文切换之间的关系
【发布时间】:2011-08-31 17:09:26
【问题描述】:

由于看起来上下文切换可能发生在指令执行的任何时候,我现在想知道为什么代码“部分有问题”(这两条指令)是有意义的,如果上下文切换可以在任何指令之间发生,我们可能会第二条指令在不同的 CPU 内核上。

void B()
  {
    Thread.MemoryBarrier();    // Barrier 3
    if (_complete)
    {
      //PART IN QUESTION
      Thread.MemoryBarrier();       // Barrier 4
      Console.WriteLine (_answer);
      //END PART IN QUESTION
    }
  }

这里关于 MemoryBarrier 的描述似乎并不能保证 MemoryBarrier 在调用它后不会切换 CPU。

(这和这个question有关)

【问题讨论】:

    标签: c# .net multithreading context-switch memory-barriers


    【解决方案1】:

    无法保证在MemoryBarrier 周围会或不会发生上下文切换。这些是正交概念。

    【讨论】:

    • 您能详细说明一下吗?如果您查看链接的问题,则 B 方法中有 2 个 memorybarrier 调用,并且它说需要第二个来确保我们在读取答案变量之前禁用缓存,以防发生上下文切换,但如果它们是正交的,我认为障碍那么 3 应该足够了还是?...
    • 它们只是不同的概念。 MemoryBarrier 是限制读写指令重新排序的能力的栅栏。他们根本无法跳过栅栏。上下文切换只是改变当前正在运行的线程。它可以在障碍之前或之后立即发生(或者真的在任何时候发生)。
    • 如果您需要保护一个变量免受多个读取器/写入器的影响,那么我建议使用lock。
    • @Valentin 我已经阅读了您的问题和参考的博客文章。真的不清楚博客文章中要实现的目标,因此很难理解他们为什么需要内存屏障。
    • 需要使用屏障 1 来防止 _completed 在 _answer 之前写入。在 _answer 之前阅读 _completed 需要障碍 4。障碍 3 的价值非常值得怀疑。如果线程完成在 C# 中也不会导致内存障碍,我会有点惊讶。如果它不是障碍 2 的原因(它将同步缓存,在 x86 上不需要,但在安腾上需要)将是,如果您在某个线程中第一个明确地运行 A,等待它完成,然后运行 ​​B,然后 B 会看到更新的值。
    【解决方案2】:

    什么保证在 Thread.MemoryBarrier() 调用后不会发生上下文切换?

    什么都没有。 MemoryBarriers 不会阻止上下文切换(或代码的原子执行)。

    关于你的另一个问题,为什么需要屏障 4:

    在上一个问题的示例代码中,如果屏障 4 不存在,C# 编译器、CLR 或 CPU 可能会在完成变量之前重新排列对 answer 变量的读取。即实际运行的代码可能类似于:

    Thread.MemoryBarrier();    // Barrier 3
    int tmpanswer = _answer;
    if (_complete)
    {
    
      Console.WriteLine (tmpanswer);
    }
    

    Console.WriteLine() 之前的障碍将阻止在读取_completed 之前读取_answer

    但请记住,代码示例仅提供关于 void B() 中的代码的这一保证(前提是 A() 仅运行一次)

    • 如果 _complete 变量为 true ,则 Console.WriteLine 将写出 123 而不是 0。

    因此,除非 A 和 B 连续运行,否则代码不会提供任何锁定/通知,因此 B 将始终打印 123。A() 和 B() 可以在其执行的任何时间交错/中断 - 你有无法控制谁可以在何时运行。

    不能保证 B() 在 A() 之后运行,无论您以哪个顺序启动 2 个线程。(尽管在代码中的其他地方,您可以先启动 A() 并明确等待它完成在开始 B() 之前)

    【讨论】:

    • 如果指令处于相同的逻辑级别,例如 {a=b; c=d; d=e;} 不像这里那样在 IF 中,总是先读完整似乎是合乎逻辑的,因为在许多情况下不需要答案。你确定这样的重新编码是可能的吗?
    • @Valentin Kuzub 内存访问重新排序与 C# 代码的逻辑/块级别无关。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-28
    • 1970-01-01
    • 2018-01-05
    • 2020-11-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-13
    相关资源
    最近更新 更多