简体   繁体   English

为什么Interlocked.Add没有超载接受双打作为参数?

[英]Why is there no overload of Interlocked.Add that accepts Doubles as parameters?

I fully appreciate the atomicity that the Threading.Interlocked class provides; 我完全理解Threading.Interlocked类提供的原子性; I don't understand, though, why the Add function only offers two overloads: one for Integers, another for Longs. 但是,我不明白为什么Add函数只提供两个重载:一个用于Integers,另一个用于Longs。 Why not Doubles, or any other numeric type for that matter? 为什么不双打,或任何其他数字类型?

Clearly, the intended method for changing a Double is CompareExchange; 显然,更改Double的预期方法是CompareExchange; I am GUESSING this is because modifying a Double is a more complex operation than modifying an Integer. 我认为这是因为修改Double比修改Integer更复杂。 Still it isn't clear to me why, if CompareExchange and Add can both accept Integers, they can't also both accept Doubles. 我还不清楚为什么,如果CompareExchange和Add都能接受整数,他们也不能同时接受双打。

Others have addressed the "why?". 其他人已经解决了“为什么?”。 It is easy however to roll your own Add(ref double, double) , using the CompareExchange primitive: 但是,使用CompareExchange原语很容易滚动自己的Add(ref double, double)

public static double Add(ref double location1, double value)
{
    double newCurrentValue = location1; // non-volatile read, so may be stale
    while (true)
    {
        double currentValue = newCurrentValue;
        double newValue = currentValue + value;
        newCurrentValue = Interlocked.CompareExchange(ref location1, newValue, currentValue);
        if (newCurrentValue == currentValue)
            return newValue;
    }
}

CompareExchange sets the value of location1 to be newValue , if the current value equals currentValue . 如果当前值等于currentValue ,则CompareExchangelocation1的值设置为newValue As it does so in an atomic, thread-safe way, we can rely on it alone without resorting to locks. 正如它以原子,线程安全的方式这样做,我们可以单独依赖它而不需要求助于锁。

Why the while (true) loop? 为什么while (true)循环? Loops like this are standard when implementing optimistically concurrent algorithms. 在实现乐观并发算法时,这样的循环是标准的。 CompareExchange will not change location1 if the current value is different from currentValue . 如果当前值与currentValue不同, CompareExchange将不会更改location1 I initialized currentValue to location1 - doing a non-volatile read (which might be stale, but that does not change the correctness, as CompareExchange will check the value). 我将currentValue初始化为location1 - 执行非易失性读取(可能是陈旧的,但这不会改变正确性,因为CompareExchange将检查该值)。 If the current value (still) is what we had read from location , CompareExchange will change the value to newValue . 如果当前值(静止)是我们从location读取的值,则CompareExchange会将值更改为newValue If not, we have to retry CompareExchange with the new current value, as returned by CompareExchange . 如果没有,我们必须使用CompareExchange返回的新当前值重试CompareExchange

If the value is changed by another thread until the time of our next CompareExchange again, it will fail again, necessitating another retry - and this can in theory go on forever, hence the loop. 如果该值被另一个线程更改,直到我们下一个CompareExchange再次出现,它将再次失败,需要再次重试 - 这理论上可以永远继续,因此循环。 Unless you are constantly changing the value from multiple threads, CompareExchange will most likely be called only once, if the current value is still what the non-volatile read of location1 yielded, or twice, if it was different. 除非您不断更改多个线程的值,否则如果当前值仍然是location1的非易失性读取产生的值,则很可能只调用一次CompareExchange ,如果不同,则更新两次。

The Interlocked class wraps around the Windows API Interlocked** functions. Interlocked类包含Windows API Interlocked **函数。

These are, in turn, wrapping around the native processor API, using the LOCK instruction prefix for x86. 反过来,它们使用x86的LOCK指令前缀环绕本机处理器API。 It only supports prefixing the following instructions: 它仅支持为以下指令添加前缀:

BT, BTS, BTR, BTC, XCHG, XADD, ADD, OR, ADC, SBB, AND, SUB, XOR, NOT, NEG, INC, DEC BT,BTS,BTR,BTC,XCHG,XADD,ADD,OR,ADC,SBB,AND,SUB,XOR,NOT,NEG,INC,DEC

You'll note that these, in turn, pretty much map to the interlocked methods. 你会注意到,这些反过来几乎映射到互锁的方法。 Unfortunately, the ADD functions for non-integer types are not supported here. 不幸的是,这里不支持非整数类型的ADD函数。 Add for 64bit longs is supported on 64bit platforms. 64位平台支持64位长的添加。

Here's a great article discussing lock semantics on the instruction level . 这是一篇很棒的文章, 讨论了指令级别的锁语义

As Reed Copsey has said, the Interlocked operations map (via Windows API functions) to instructions supported directly by the x86/x64 processors. 正如Reed Copsey所说,Interlocked操作映射(通过Windows API函数)到x86 / x64处理器直接支持的指令。 Given that one of those functions is XCHG, you can do an atomic XCHG operation without really caring what the bits at the target location represent. 鉴于其中一个函数是XCHG,您可以执行原子XCHG操作,而无需真正关心目标位置的位代表什么。 In other words, the code can "pretend" that the 64-bit floating point number you are exchanging is in fact a 64-bit integer, and the XCHG instruction won't know the difference. 换句话说,代码可以“假装”您正在交换的64位浮点数实际上是64位整数,并且XCHG指令不会知道差异。 Thus, .Net can provide Interlocked.Exchange functions for floats and doubles by "pretending" that they are integers and long integers, respectively. 因此,.Net可以通过“假装”它们分别是整数和长整数来为浮点数和双精度数提供Interlocked.Exchange函数。

However, all of the other operations actually do operate on the individual bits of the destination, and so they won't work unless the values actually represent integers (or bit arrays in some cases.) 但是,所有其他操作实际上都对目标的各个位进行操作,因此除非值实际表示整数(或某些情况下的位数组),否则它们将无法工作。

I suspect that there are two reasons. 我怀疑有两个原因。

  1. The processors targeted by .Net support interlocked increment only for integer types. .Net所针对的处理器仅支持整数类型的互锁增量。 I believe this is the LOCK prefix on x86, probably similar instructions exist for other processors. 我相信这是x86上的LOCK前缀,可能存在类似于其他处理器的指令。
  2. Adding one to a floating point number can result in the same number if it is big enough, so I'm not sure if you can call that an increment. 如果它足够大,将一个加到浮点数可能会产生相同的数字,所以我不确定你是否可以调用该增量。 Perhaps the framework designers are trying to avoid nonintuitive behavior in this case. 也许框架设计者试图在这种情况下避免非直观的行为。

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

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