5

任意の値によるインクリメントをサポートするスレッドセーフなカウンターの実装を探していたInterlockedところ、ドキュメントから直接このサンプルを見つけましたInterlocked.CompareExchange(簡単にするためにわずかに変更されています)。

private int totalValue = 0;

public int AddToTotal(int addend)
{
    int initialValue, computedValue;
    do
    {
        // How can we get away with not using a volatile read of totalValue here?
        // Shouldn't we use CompareExchange(ref TotalValue, 0, 0)
        // or Thread.VolatileRead
        // or declare totalValue to be volatile?           
        initialValue = totalValue;

        computedValue = initialValue + addend;

    } while (initialValue != Interlocked.CompareExchange(
        ref totalValue, computedValue, initialValue));

    return computedValue;
}

 public int Total
 {
    // This looks *really* dodgy too, but isn't 
    // the target of my question.
    get { return totalValue; }
 }

このコードが何をしようとしているのかはわかりますが、追加された一時変数に代入するときに、共有変数の揮発性読み取りを使用しないことでどのように回避できるかわかりません。

initialValueループ全体で古い値を保持し、関数が返されない可能性はありますか? それとも、メモリバリア (?)CompareExchangeによってそのような可能性は排除されますか? 任意の洞察をいただければ幸いです。

編集CompareExchange:の後続の読み取りが最後の呼び出しtotalValueの時点で最新である場合、このコードは問題ないことを理解していることを明確にする必要があります。しかし、それは保証されていますか? CompareExchange

4

3 に答える 3

2

古い値を読み取った場合、CompareExchangeは交換を実行しません。基本的には、「値が実際に計算に基づいた値である場合にのみ操作を行う」ということです。ある時点で正しい値が得られれば問題ありません。同じ古い値を永遠に読み続けた場合は問題になるため、チェックに合格するCompareExchange ことCompareExchangeはありませんが、メモリの障壁が、少なくともループを経た後、最新の値を読み取ることを意味していると強く思います価値。起こりうる最悪の事態は永久に循環することです - 重要な点は、間違った方法で変数を更新することはできないということです。

Total(そして、はい、プロパティが危険であるというあなたの意見は正しいと思います。)

編集:別の言い方をすると:

CompareExchange(ref totalValue, computedValue, initialValue)

意味: 「現在の状態が実際に だった場合initialValue、私の計算は有効であり、それを に設定する必要がありますcomputedValue。」

現在の状態は、少なくとも 2 つの理由で間違っている可能性があります。

  • 割り当てはinitialValue = totalValue;、別の古い値を持つ古い読み取りを使用しました
  • その任務のtotalValue 、何かが変わった

これらの状況を別の方法で処理する必要はまったくありません-したがって、ある時点で最新の値が表示されるようになる限り、「安価な」読み取りを行うことは問題ありません...そして、メモリの障壁が関係していると思いますinは、ループ ラウンドするときに、表示される古い値が以前の呼び出しとCompareExchange同じくらい古いことを保証します。CompareExchange

編集: 明確にするために、私はサンプルが正しいと思い CompareExchangeますtotalValue. そうでない場合 -totalValueループを一周し続けるときの任意の古い値をまだ読み取ることができる場合 - コードは実際に壊れており、決して終了しない可能性があります。

于 2011-09-26T14:40:22.603 に答える
2

管理対象は、Win32 APIInterlocked.CompareExchangeの に直接マップされます ( 64 ビット バージョンInterlockedCompareExchangeもあります)。

関数シグネチャでわかるように、ネイティブ API では変換先が volatile である必要があり、マネージ API では必須ではありませんが、Joe Duffy が著書Concurrent Programming on Windowsで volatile を使用することを推奨しています。

于 2011-09-26T15:28:14.453 に答える