C# 试图理解Thread.MemoryBarrier()和上下文切换之间的关系

C# 试图理解Thread.MemoryBarrier()和上下文切换之间的关系,c#,.net,multithreading,context-switch,memory-barriers,C#,.net,Multithreading,Context Switch,Memory Barriers,既然上下文切换似乎可能发生在指令执行的任何时候,我现在想知道为什么代码“部分有问题”(这两条指令)是有意义的,如果上下文切换可以发生在任何指令之间,并且我们可能在第二条指令的不同CPU核上 void B() { Thread.MemoryBarrier(); // Barrier 3 if (_complete) { //PART IN QUESTION Thread.MemoryBarrier(); // Barrier 4

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

void B()
  {
    Thread.MemoryBarrier();    // Barrier 3
    if (_complete)
    {
      //PART IN QUESTION
      Thread.MemoryBarrier();       // Barrier 4
      Console.WriteLine (_answer);
      //END PART IN QUESTION
    }
  }
这里对MemoryBarrier的描述似乎并不能保证CPU在调用后不会被切换


(这与此相关)

不能保证上下文切换会或不会在
内存载体周围发生。这些是正交概念

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

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

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

在上一个问题的示例代码中,如果barrier 4不存在,C#编译器、CLR或CPU可能会将答案变量的读取重新排序到完成变量之前。i、 e.实际运行的代码可能类似于:

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

  Console.WriteLine (tmpanswer);
}
Console.WriteLine()前的障碍物将阻止在读取
\u已完成之前读取
\u answer

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

  • 如果_complete变量为true,则Console.WriteLine将写出123而不是0
因此,除非A和B连续运行,否则代码不会提供任何锁定/通知,以便B始终打印123。A()和B()可以在执行过程中随时交错/中断-您无法控制何时由谁运行


无法保证B()在A()之后运行,无论您以何种顺序启动这两个线程。(当然,在代码中的其他地方,您可以先启动A(),然后显式地等待它完成,然后再启动B())

那么,您能详细说明一下吗?如果你看一看链接问题,B方法中有两个memorybarrier调用,据说第二个调用是为了确保我们在读取答案变量之前禁用缓存,以防发生上下文切换,但如果它们是正交的,我认为屏障3应该足够了,或者?…它们只是不同的概念。MemoryBarrier是一种围栏,它限制了读写指令的能力,从而无法重新排序。他们根本不能跳过篱笆。上下文切换只是更改当前正在运行的线程。如果你需要保护一个变量不受多个读写器的攻击,那么我建议使用
@Valentin我已经阅读了你的问题和引用的博客文章。我们真的不清楚在博客文章中要达到什么目的,因此很难理解他们为什么需要记忆障碍。障碍1是为了防止在回答之前完成写作。障碍4需要在回答之前阅读完成的内容。屏障3的价值非常值得怀疑。如果线程整理在C#中不会造成内存障碍,我会有点惊讶。如果没有,Barrier 2(同步缓存,x86上不需要,但安腾上需要)的原因是,如果您在某个地方显式运行一个线程中的first-in-one,等待它完成,然后运行B,那么B将看到更新的值。我认为如果指令位于相同的逻辑级别,如{A=B;c=d;d=e;},则可能会发生重新排序如果像这里这样的话就不需要了,因为在很多情况下,答案是不需要的。你确定这种重新排序是可能的吗?@Valentin Kuzub内存访问重新排序与C代码的逻辑/块级别无关。