Interlocked是否保证C#中其他线程的可见性,还是仍然需要使用volatile?

Kir*_*ril 6 c# multithreading volatile interlocked thread-safety

我一直在阅读类似问题的答案,但我仍然有点困惑......亚伯有一个很好的答案,但这是我不确定的部分:

...声明变量volatile会使每次访问都变得不稳定.不可能以任何其他方式强制执行此行为,因此不能用Interlocked替换volatile.在其他库,接口或硬件可以访问您的变量并随时更新它或需要最新版本的情况下,需要这样做.

是否Interlocked保证对所有线程的原子操作的可见性,或者我是否仍然必须volatile在值上使用关键字以保证对更改的可见性?

这是我的例子:

volatile int value = 100000; // <-- do I need the volitile keyword
// ....

public void AnotherThreadMethod()
{
 while(Interlocked.Decrement(ref value)>0)
 {
  // do something
 }
}


public void AThreadMethod()
{
 while(value > 0)
 {
  // do something
 }
}
Run Code Online (Sandbox Code Playgroud)

更新:
我是一个糟糕的运动,我改变了原来的例子,所以这里又是:

public class CountDownLatch
{
    private volatile int m_remain; // <--- do I need the volatile keyword here?
    private EventWaitHandle m_event;

    public CountDownLatch(int count)
    {
        Reset(count);
    }

    public void Reset(int count)
    {
        if (count < 0)
            throw new ArgumentOutOfRangeException();
        m_remain = count;
        m_event = new ManualResetEvent(false);
        if (m_remain == 0)
        {
            m_event.Set();
        }
    }

    public void Signal()
    {
        // The last thread to signal also sets the event.
        if (Interlocked.Decrement(ref m_remain) == 0)
            m_event.Set();
    }

    public void Wait()
    {
        m_event.WaitOne();
    }
}
Run Code Online (Sandbox Code Playgroud)

Rem*_*anu 5

它们不需要*波动率,因为你永远不会检查互锁变量的值.相反,您始终检查互锁操作返回的值.混合互锁操作和普通分配/比较总是导致错误的代码.

我不确定Reset()函数的意图是什么,但是这段代码在线程间原语中没有位置:你分配给m_remain,你直接检查m_remain的值,非常糟糕.我强烈建议你把它拿出来:不仅实现不正确,而且我非常怀疑需要"重置"计数器中期寿命的语义.保持简单:ctor(将代码从Reset移入其中)Signal和Wait是唯一需要的三个运算符,它们现在是正确的.

更新编辑代码后.

忽略你不应该混合两者的事实,如果你最终混合它们然后是,仍然需要挥发性.易失性主要是关于IL代码和生成的JIT代码,以确保始终从实际内存位置读取值,并且不进行优化,例如代码重新排序.使用Interlocked操作更新值的不相关代码对读取该值的其他部分没有影响.对于volatile属性,编译器/ JIT仍然可以生成忽略其他地方发生的写入的代码,如果写入是互锁或直接赋值则无关紧要.

顺便说一句,有一些有效的模式混合普通的读取和互锁操作,但它们通常涉及Interlocked.CompareExchange,并且像这样:读取当前状态,根据当前状态进行一些计算,尝试将状态替换为互锁比较交换:如果成功,如果没有删除计算结果并返回步骤1.