[英]Combining Interlocked.Increment and Interlocked.Exchange
[英]Combining Interlocked.Increment and Volatile.Write
我一直在查看 System.Reactive ( 这里)的源代码,它把我带到了这个地方,在同一个变量上有一个Volatile.Read
后跟Interlocked.CompareExchange
:
if (Volatile.Read(ref _runDrainOnce) == 0
&& Interlocked.CompareExchange(ref _runDrainOnce, 1, 0) == 0)
{
//do something
}
当我阅读它时,其逻辑是“如果 runDrainOnce 为 0,并且在我将其更改为 1 之前它为零”。 这里有什么微妙的吗? 为什么第一次检查不是多余的?
(更令人难以置信的是,在同一个函数中有一个lock
和一个Monitor.Pulse
。这是下注的结果吗?:))
整个功能:
private void Schedule()
{
// Schedule the suspending drain once
if (Volatile.Read(ref _runDrainOnce) == 0
&& Interlocked.CompareExchange(ref _runDrainOnce, 1, 0) == 0)
{
_drainTask.Disposable = _scheduler.ScheduleLongRunning(this, DrainLongRunning);
}
// Indicate more work is to be done by the drain loop
if (Interlocked.Increment(ref _wip) == 1L)
{
// resume the drain loop waiting on the guard
lock (_suspendGuard)
{
Monitor.Pulse(_suspendGuard);
}
}
}
完全投机的可能性随之而来。 实际上,我会说如果没有明确的基准测试,这可能无关紧要,我们可能会简单地丢失Volatile.Read
测试,甚至只是使用lock
。 如果有明确的标杆,我希望在那个评论暗示(或链接)。
我们可以从命名 ( _runDrainOnce
) 推断出我们只希望这会成功一次,如果某件事只会成功一次,我们真的不需要成功案例是超级最优的——所以:有一个冗余测试成功之路:问题不大。 相比之下,让我们推测失败场景被多次调用,因此仅执行受保护的读取(不尝试写入)而使其失败可能是有益的。
一切都调用Schedule
代码 - 参见OnCompleted
、 OnError
、 OnNext
等 - 所以大概目的是确保调度开始,尽可能有效 - 所以它不会超过必要的接触Intelocked
(一旦成功,并且可能有几次表示失败,如果最初存在高线程竞争)
您没有明确询问,但lock
/ Pulse
是一种常见模式,用于让空闲工作循环等待使用Monitor
接收工作; 你需要唤醒它,如果它很可能闲置,也就是当计数为零,现在是非零(因此Interlocked.Increment
)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.