请观察:
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()
(或者至少在我看来这样做)。如果我告诉我理解它们是如何工作的,我会说谎。
有人可以在我的代码中解释我是否需要它们以及为什么?
P.S。
我在几个地方重复相同的模式。所以我想介绍以下方法:
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
答案是否相同?
答案 0 :(得分:1)
不,您不需要使用volatile
或内存障碍,因为Interlocked.CompareExchange
方法隐式生成完整的栅栏,这会阻止指令重新排序和变量缓存。
我建议阅读: