假设我有一个变量“计数”,和有几个线程访问,并通过使用互锁设置“计数器”的值,即:

int value = Interlocked.Increment(ref counter);

int value = Interlocked.Decrement(ref counter);

我可以假设的是,通过互锁所做的更改将在所有线程是可见的?

如果不是,我应该怎么做才能让所有的线程同步的变量?

编辑:有人建议我用波动。但是,当我设置了“计数器”的易失性,有编译器警告“参照挥发性字段将不被作为挥发性处理”。

当我读在线帮助,它说,“易失性字段通常不应使用ref或out参数传递”。

有帮助吗?

解决方案

InterlockedIncrement /递减上的x86 CPU(86的锁定添加/ DEC)被自动创建的存储器屏障其给出清楚了解所有线程(即,所有线程可以看到它的更新如在阶状连续存储器一致性)。存储器屏障使得可以完成所有未决存储器的加载/存储。虽然C#和Java(和一些C / C ++编译器)的强制volatile使存储器屏障volatile是不相关的这个问题。但是,互锁操作已经由CPU具有记忆障碍。

请也看看我的另一个答案在计算器。

请注意,我有假设C#的InterlockedIncrement /递减是固有的映射86的锁定添加/ DEC

其他提示

  

我可以假设的是,通过互锁所做的更改将在所有线程是可见的?

这取决于你如何阅读价值。如果你“只是”读它,则没有,这不会总是,除非你将其标记为挥发性在其他线程可见。这会导致一个恼人的警告虽然。

作为替代(和非常优选IMO),使用另一个互锁指令读取它。这将始终看到所有的线程中更新值:

int readvalue = Interlocked.CompareExchange(ref counter, 0, 0);

,其返回值的读取,并且,如果它是0 0。互换它

动机:警告暗示东西是不正确;结合这两种技术(易失性和互锁)不这样做的预期方式。

更新:似乎另一种方法可靠32位读操作,而无需使用“挥发性”是通过使用Thread.VolatileRead此答案提示。也有一些证据表明,我完全错了有关使用Interlocked 32位读取,例如的此连接问题时,虽然不知的区别本质上是有点迂腐。

我真正的意思是:不使用这个答案你的唯一来源;我在这个我的疑惑。

实际上,它们不是。如果你想安全地修改counter,那么你正在做正确的事情。但是,如果你想读counter直接,你需要将其声明为volatile。否则,编译器没有理由相信counter会改变,因为Interlocked操作代码,它可能看不到。

联锁确保只有1在时间线程可以更新该值。为了确保其他线程能够读取正确的值(而不是高速缓存的值)将其标记为易失性。

公共挥发性INT计数器;

没有;一个的互锁-AT-只写本身并确保在代码可变读取实际上新鲜; 不正确地从一个字段读以及一个节目的可能不是线程安全的,即使在一个“强有力的内存模型”。这适用于任何形式分配给线程之间共享的字段的

下面是的代码不会终止由于JIT 的例子。 (它是从在记忆障碍。 NET 是更新的问题的可运行LINQPad程序)。

// Run this as a LINQPad program in "Release Mode".
// ~ It will never terminate on .NET 4.5.2 / x64. ~
// The program will terminate in "Debug Mode" and may terminate
// in other CLR runtimes and architecture targets.
class X {
    // Adding {volatile} would 'fix the problem', as it prevents the JIT
    // optimization that results in the non-terminating code.
    public int terminate = 0;
    public int y;

    public void Run() {
        var r = new ManualResetEvent(false);
        var t = new Thread(() => {
            int x = 0;
            r.Set();
            // Using Volatile.Read or otherwise establishing
            // an Acquire Barrier would disable the 'bad' optimization.
            while(terminate == 0){x = x * 2;}
            y = x;
        });

        t.Start();
        r.WaitOne();
        Interlocked.Increment(ref terminate);
        t.Join();
        Console.WriteLine("Done: " + y);
    }
}

void Main()
{
    new X().Run();
}

在记忆障碍的说明.NET

  

这一次是JIT,而不是硬件。很明显,JIT已经高速缓存了变量的值终止[EAX寄存器和]程序现在卡在环上方突出显示。

     

或者使用一个或lock添加while循环中Thread.MemoryBarrier将解决这个问题。或者,你甚至可以用Volatile.Read [或volatile场。 这里的记忆障碍的目的只是抑制JIT优化。的现在,我们已经看到了怎样的软件和硬件可以重新排序存储器操作的,它的时间来讨论记忆障碍..

<强>也就是说,额外的屏障构建体被要求在读出侧至防止问题用汇编和JIT重排序/优化这是一个不同的问题比内存一致性!

在这里添加volatile会的阻止的JIT的优化,因此“解决问题”,即使在警告这样的结果。该程序也可以通过使用Volatile.Read的或者使屏障各种其它操作中的一个校正:这些障碍是CLR / JIT程序的正确性作为底层硬件存储器围栏的尽可能多的部分

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top