Ben*_*jol 6 c# concurrency volatile
我一直在查看 System.Reactive 的源代码(这里),它把我带到了这个地方,在同一个变量上有一个Volatile.Read后跟Interlocked.CompareExchange, :
if (Volatile.Read(ref _runDrainOnce) == 0
&& Interlocked.CompareExchange(ref _runDrainOnce, 1, 0) == 0)
{
//do something
}
Run Code Online (Sandbox Code Playgroud)
当我阅读它时,其逻辑是“如果 runDrainOnce 为 0,并且在我将其更改为 1 之前它为零”。这里有什么微妙的吗?为什么第一次检查不是多余的?
(更令人难以置信的是,在同一个函数中有 alock和 a 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
完全投机的可能性随之而来。实际上,我会说如果没有明确的基准测试,这可能无关紧要,我们可能会简单地丢失Volatile.Read测试,甚至只是使用lock. 如果有很明确的标杆,我希望在那个评论暗示(或链接)。
我们可以从命名 ( _runDrainOnce)推断出我们只希望这会成功一次,如果某事只会成功一次,我们真的不需要成功案例是超级最优的——所以:在成功中进行冗余测试路径:不是一个大问题。相比之下,让我们推测失败场景被多次调用,因此仅执行受保护的读取(不尝试写入)而使其失败可能是有益的。
该Schedule代码由所有内容调用- 请参阅OnCompleted、OnError、OnNext等 - 所以大概目的是确保调度开始,尽可能高效 - 所以它不会Intelocked超过必要的(一旦成功,可能还有一些表示失败的次数(如果最初存在高线程竞争)
您没有明确询问,但lock/Pulse是使用Monitor;等待接收工作的空闲工作循环的常见模式;你需要唤醒它,如果它很可能闲置,也就是当数是零,现在是非零(因此Interlocked.Increment)。
| 归档时间: |
|
| 查看次数: |
113 次 |
| 最近记录: |