28

Threading.Interlockedクラスが提供するアトミック性に完全に感謝します。ただし、Add関数が2つのオーバーロードしか提供しない理由はわかりません。1つは整数用、もう1つはLong用です。Doubles、またはその他の数値タイプを使用しないのはなぜですか?

明らかに、Doubleを変更するための意図された方法はCompareExchangeです。これは、Doubleの変更は、Integerの変更よりも複雑な操作であるためだと思います。それでも、CompareExchangeとAddの両方が整数を受け入れることができる場合、両方がDoubleを受け入れることができない理由は、私にはわかりません。

4

4 に答える 4

41

他の人は「なぜ?」に対処しました。ただし、プリミティブAdd(ref double, double)を使用して独自の をロールするのは簡単です。CompareExchange

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現在の値が に等しい場合、 の値をlocation1に設定します。これはアトミックでスレッドセーフな方法で行われるため、ロックに頼ることなく単独で信頼できます。newValuecurrentValue

なぜwhile (true)ループ?このようなループは、楽観的並行アルゴリズムを実装する場合の標準です。現在の値が と異なる場合、CompareExchangeは変化しません。私は初期化しました- 不揮発性読み取りを行います (これは古い可能性がありますが、値をチェックするため、正確性は変わりません)。現在の値 (まだ) が から読み取った値である場合、は値を に変更します。そうでない場合は、によって返される新しい現在の値で再試行する必要があります。location1currentValuecurrentValuelocation1CompareExchangelocationCompareExchangenewValueCompareExchangeCompareExchange

次の時間までに別のスレッドによって値が変更されると、CompareExchange再び失敗し、別の再試行が必要になります。これは理論的には永遠に続く可能性があるため、ループが発生します。複数のスレッドから常に値を変更している場合を除き、CompareExchangeは、現在の値がまだ不揮発性読み取りによってlocation1得られたものである場合は 1 回だけ呼び出され、異なる場合は 2 回呼び出されます。

于 2013-06-03T09:27:35.013 に答える
29

Interlocked クラスは、Windows API の Interlocked** 関数をラップします。

これらは、x86 の LOCK 命令プレフィックスを使用して、ネイティブ プロセッサ API をラップします。次の命令のプレフィックスのみをサポートします。

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

これらは、インターロックされたメソッドにほぼ対応していることに気付くでしょう。残念ながら、非整数型の ADD 関数はここではサポートされていません。64 ビット long の追加は、64 ビット プラットフォームでサポートされています。

これは、命令レベルでのロック セマンティクスについて説明している素晴らしい記事です。

于 2009-09-09T15:58:23.757 に答える
7

Reed Copsey が言ったように、インターロック操作は (Windows API 関数を介して) x86/x64 プロセッサによって直接サポートされる命令にマップされます。これらの機能の 1 つが XCHG であるとすると、ターゲット位置のビットが何を表しているかを実際に気にすることなく、アトミックな XCHG 操作を実行できます。言い換えると、コードは、交換している 64 ビット浮動小数点数が実際には 64 ビット整数であると「ふりをする」ことができ、XCHG 命令はその違いを認識しません。したがって、.Net は、float と double がそれぞれ整数と long 整数であると「ふりをする」ことによって、Interlocked.Exchange 関数を提供できます。

ただし、他のすべての操作は実際には宛先の個々のビットに対して操作を行うため、値が実際に整数 (場合によってはビット配列) を表さない限り機能しません。

于 2011-06-27T23:37:00.687 に答える
1

2つの理由があると思います。

  1. .Net が対象とするプロセッサは、整数型に対してのみインターロック インクリメントをサポートします。これは x86 の LOCK プレフィックスだと思います。おそらく、他のプロセッサにも同様の命令が存在します。
  2. 浮動小数点数に 1 を加算すると、それが十分に大きい場合は同じ数になる可能性があるため、それをインクリメントと呼べるかどうかはわかりません。おそらく、フレームワークの設計者は、この場合、非直感的な動作を回避しようとしています。
于 2009-09-09T15:52:02.127 に答える