我一直在阅读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();
}
}
答案 0 :(得分:5)
他们不需要**波动,因为你从不检查互锁变量的值。相反,您始终通过互锁操作检查值返回。混合互锁操作和普通分配/比较总是会导致代码错误。
我不确定Reset()函数的意图是什么,但是这段代码在线程间原语中没有位置:你分配给m_remain,你直接检查m_remain的值,非常糟糕。我强烈建议你把它拿出来:不仅实现不正确,而且我非常怀疑需要“重置”计数器中期寿命的语义。保持简单:ctor(将代码从Reset移动到其中)Signal和Wait是唯一需要的三个运算符,它们现在是正确的。
已更新编辑代码后。
忽略你不应该混合两者的事实,如果你最终混合它们然后是,仍然需要挥发性。易失性主要是关于IL代码和生成的JIT代码,以确保始终从实际内存位置读取值,并且不进行优化,例如代码重新排序。使用Interlocked操作更新值的不相关代码对读取该值的其他部分没有影响。如果没有volatile
属性,编译器/ JIT仍然可以生成忽略其他地方发生的写入的代码,如果写入是互锁或直接赋值则无关紧要。
答案 1 :(得分:1)
我认为System.Threading.Thread.VolatileRead(ref myVariable)可能正是您所寻找的。与Interlocked.Increment一起使用时,它可用于保证更改是原子的,并且您读取的值是最新的。