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

时间:2010-03-19 05:48:45

标签: c# multithreading thread-safety volatile interlocked

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

  

...声明变量volatile会使每个变量都变得不稳定   访问。这是不可能的   任何其他方式的行为,因此不稳定   不能用Interlocked取代。   在需要的情况下需要这样做   其他库,接口或   硬件可以访问您的变量和   随时更新,或者最需要   最新版本。

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
 }
}

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

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();
    }
}

2 个答案:

答案 0 :(得分:5)

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

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

已更新编辑代码后。

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

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

答案 1 :(得分:1)

我认为System.Threading.Thread.VolatileRead(ref myVariable)可能正是您所寻找的。与Interlocked.Increment一起使用时,它可用于保证更改是原子的,并且您读取的值是最新的。