繁体   English   中英

结合 Interlocked.Increment 和 Volatile.Write

[英]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代码 - 参见OnCompletedOnErrorOnNext等 - 所以大概目的是确保调度开始,尽可能有效 - 所以它不会超过必要的接触Intelocked (一旦成功,并且可能有几次表示失败,如果最初存在高线程竞争)


您没有明确询问,但lock / Pulse是一种常见模式,用于让空闲工作循环等待使用Monitor接收工作; 你需要唤醒它,如果它很可能闲置,也就是当计数为零,现在是非零(因此Interlocked.Increment )。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM