9

下面是一个基于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
}

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

假设两个线程竞相执行此方法,第一个线程立即成功,而第二个线程最初没有更改,必须重复。在没有其他竞争者的情况下,第二个线程是否有可能在第二次尝试时失败

如果示例的第二个线程在第二次尝试时不能失败,那么我们可以通过 a 获得SpinWait什么?万一有一百个线程竞相执行该方法,是否可以缩短几个周期?

4

2 回答 2

2

我的非专家意见是,在这种特殊情况下,两个线程偶尔调用AddIfLessThanaSpinWait是不需要的。如果两个线程都AddIfLessThan在一个紧密的循环中调用,这可能是有益的,这样每个线程都可以在几微秒内不间断地进行进程。

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

于 2019-11-04T17:27:00.347 回答
2

两个线程不是SpinWait讨论的主题。但是这段代码并没有告诉我们实际上有多少线程可以竞争资源,并且使用相对较多的线程SpinWait可以变得有益。特别是随着线程数量的增加,试图成功获取资源的线程虚拟队列会变得更长,而那些恰好最终被服务的线程很有可能超过调度程序分配的时间片,这反过来又可以导致更高的 CPU 消耗,并可能影响其他调度线程的执行,即使具有更高的优先级。这SpinWait通过设置一些允许旋转的上限,在此之后将执行上下文切换,可以很好地解决这种情况。因此,在需要进行昂贵的系统调用以触发上下文切换和不受控制的用户模式 ​​CPU 消耗之间进行合理的权衡是合理的,后者在某些情况下可能会影响其他线程的执行。

于 2019-11-04T20:50:50.937 回答