.net 使用Interlocked.CompareExchange的以下示例是否需要易失性和/或内存屏障?

.net 使用Interlocked.CompareExchange的以下示例是否需要易失性和/或内存屏障?,.net,multithreading,.net,Multithreading,请注意: private DateTime m_lastTimeUtc; private int m_isInProgress; ... if (DateTime.UtcNow - m_lastTimeUtc >= m_interval) { if (Interlocked.CompareExchange(ref m_isInProgress, 1, 0) == 0) { if (DateTime.UtcNow - m_lastTimeUtc >= m

请注意:

private DateTime m_lastTimeUtc;
private int m_isInProgress;
...
if (DateTime.UtcNow - m_lastTimeUtc >= m_interval)
{
    if (Interlocked.CompareExchange(ref m_isInProgress, 1, 0) == 0)
    {
        if (DateTime.UtcNow - m_lastTimeUtc >= m_interval)
        {
            m_lastTimeUtc = DateTime.UtcNow;
            // Do the work
            m_lastTimeUtc = DateTime.UtcNow;
            Interlocked.Exchange(ref m_isInProgress, 0);
        }
    }
}
此代码的预期语义如下所示:

  • 代码可能会被频繁调用——每秒数百次
  • 它应该或多或少地定期做某些工作。如果代码没有被频繁调用,那么实际的周期可能非常不规则,这是正常的。但是如果经常调用它(通常是这样),那么工作就会非常有规律地完成
  • 点击
    m_isInProgress=1
    的线程将跳过该工作,这是完全正常的
当然,环境是多线程的

我注意到人们在做类似于我在这里所做的事情时(或者至少在我看来是这样),正在使用
volatile
关键字和/或调用
Interlocked.MemoryBarrier()
。如果我说我了解它们的工作原理,那我就是在撒谎

有人能解释一下我的代码中是否需要它们以及为什么吗

附言

我在几个地方重复了同样的模式。因此,我想介绍以下方法:

private static void DoWork(TimeSpan interval, ref DateTime lastTimeUtc, ref int isInProgress, Action work)
{
    if (DateTime.UtcNow - lastTimeUtc >= interval && Interlocked.CompareExchange(ref isInProgress, 1, 0) == 0 && DateTime.UtcNow - lastTimeUtc >= interval)
    {
        lastTimeUtc = DateTime.UtcNow;
        work();
        lastTimeUtc = DateTime.UtcNow;
        Interlocked.Exchange(ref isInProgress, 0);
    }
}
鉴于此方法,我打算这样调用它:

DoWork(m_interval, ref m_lastTimeUtc, ref m_isInProgress, () =>
{
  // Do the work
});

对于
DoWork
,答案是否相同?

否,您不需要使用
volatile
或内存屏障,因为
是互锁的。CompareExchange
方法隐式生成完整的围栏,这会阻止指令重新排序和变量缓存

我建议您阅读以下内容: