基于CompareExchange的互锁实施是否应该使用SpinWait?

Tim*_*imo 8 c# multithreading interlocked compare-and-swap spinwait

以下是基于的互锁方法的实现Interlocked.CompareExchange

这段代码SpinWait在重复之前是否宜使用自旋?

public static bool AddIfLessThan(ref int location, int value, int comparison)
{
    int currentValue;
    do
    {
        currentValue = location; // Read the current value
        if (currentValue >= comparison) return false; // If "less than comparison" is NOT satisfied, return false
    }
    // Set to currentValue+value, iff still on currentValue; reiterate if not assigned
    while (Interlocked.CompareExchange(ref location, currentValue + value, currentValue) != currentValue);
    return true; // Assigned, so return true
}
Run Code Online (Sandbox Code Playgroud)

我已经看到SpinWait在这种情况下使用过,但是我的理论是它应该是不必要的。毕竟,循环仅包含少量指令,并且总是有一个线程在进行中。

假设有两个线程竞相执行此方法,并且第一个线程立即成功执行,而第二个线程最初不做任何更改,必须重申。没有其他竞争者,第二个线程是否有可能在第二次尝试时失败

如果该示例的第二个线程在第二次尝试上不能失败,那么使用?可以得到什么SpinWait?如果不太可能发生一百个线程争分夺秒地执行该方法的情况,可以省去几个周期?

The*_*ias 3

我的非专家意见是,在这种特殊情况下,两个线程偶尔调用AddIfLessThan, aSpinWait是不需要的。如果两个线程都在调用,这可能会很有用AddIfLessThan在紧密循环中调用,则这可能是有益的,这样每个线程都可以在某些 \xce\xbcsec 内不间断地取得进展。

\n\n

AddIfLessThan实际上,我做了一个实验,测量了在紧密循环中调用一个线程与两个线程的性能。两个线程需要几乎四倍的时间才能进行相同数量的循环(累计)。添加SpinWait到混合中使得两个线程仅比单线程慢一点。

\n

  • @TheodorZoulias:您可以在 IL 主体中声明跳过当地人的 init,这应该会给您带来几乎性能的恢复。您需要为此发出 IL 代码,因为尚无语言支持。请参阅 https://github.com/dotnet/csharplang/blob/master/proposals/skip-localsinit.md 当您想要旋转一次时,您可以对其调用 Reset,然后调用 SpinOnce。 (3认同)
  • @IanRingrose 它使它慢了 15%。我在`AddIfLessThan`方法中声明了一个`SpinWait`类型的变量,虽然它是一个值类型并且它的`SpinOnce`方法从未被调用,但它仍然增加了一些开销。 (2认同)