web-dev-qa-db-ja.com

アトミックカウンターの実装戦略

基本的に、デクリメントされ、頻繁にポーリングされるカウンター変数があります。それを実装するには、3つのオプションがあります。

番号1:

private volatile int count;
public void Finished() {
    Interlocked.Decrement(ref count);
}

public bool Done() {
    return count == 0;
}

2番:

private int count;
public void Finished() {
    Interlocked.Decrement(ref count);
}

public bool Done() {
    return Volatile.Read(ref count) == 0;
}

番号3:

struct AtomicInteger {
    private int value;

    public int Get() {
        return Volatile.Read(ref value);
    }
    // other methods
}
// In actual class:
private AtomicInteger count;
public bool Done() {
     return count.Get() == 0;
}

最初のものは、揮発性変数を参照渡しするため、コンパイラ警告を出します。一方、変数の定義から、変数が同時にアクセスされることは明らかです。これは、コードを見る人にとっては明らかに警告のサインです。実際に長い時間が必要だと判断した場合、この方法は機能しなくなります。

2番目は警告を出しませんが、private int countを読んだだけでは、変数が同時に使用されていることが明確ではないため、コメントで明示的に記述する必要があります(実際に通常のVolatile.Readの代わりにアサーションを読み込みます-見落としがちなことです。)一方、変数をlongに変更しても、追加の作業は発生しません。

私は3番が好きで、インライン化した後、他のソリューションと比較してパフォーマンスのオーバーヘッドが発生しないはずです。欠点:すべてのプリミティブのコードの重複。一方、コードは非常に単純で、問題を心配することなくコピーして貼り付けることができ、変更されることはほとんどありません。

私が見逃してきた各メソッドの長所/短所についてのコメント、またはC#で慣用的なものと見なされるものはありますか?

3
Voo

スレッドセーフが非常にトリッキーな問題であることを考えると、このコードの正確さについてコメントすることはできません。エラーを見つけた場合はお知らせください。回答が編集または削除されます。

C#のさまざまなメモリセマンティクス修飾子の目的の概要については、以下を参照してください。

  • SO: 揮発性vs.インターロックvs.ロック
  • 注:受け入れられた回答を額面どおりに受け取らないでください。 (特にC#仕様とフレームワークの作成者からの)参照リンクを読んで、自分で決めてください。

また推奨:

また、Joe DuffyのVolatileクラスis .NET 4.5の一部

また、「あるべき」と「それが実装されている方法」の違いにも注意してください。実用的には、後者のみが重要です。


@Euphoricからのフィードバックに基づいて、OPのNumber 2は、OPの要件を満たすソリューションであるようです。

コードをクラスにラップする(つまり番号)は、コーディングスタイルとリファクタリングの問題です。

2
rwong

それらのどれも実際にはアトミックではありません。慣用的なアプローチは次のようなものになります

class Countdown {
  private int count;

  public Countdown(int ct) {
    count = ct; 
  }

 public bool Do(Action stuff) { // or whatever good name makes sense; possibly Func<T> returning T not bool
     var isDone = Interlocked.Decrement(ref count) == 0;
     if (!isDone) {
          // possibly try/catch
          stuff();
          return true;
     }

     return false;
  }
}
0
Telastyn

私はナンバーワンがC#で慣用的であると言うでしょう。

個人的に私は2番目を好みます。「揮発性読み取り」情報をフィールド宣言に埋め込むのではなく、読み取り自体に移動するのが好きです。 C#のvolatile修飾子は奇妙で誤解を招くものであるだけでなく、誤解されることが多いため、初心者がVolatile.Readのドキュメントを読んで理解する方が簡単だと思います。最後に、暗黙のメモリフェンスを強調する必要があるより複雑なケースでは、これは健康的な習慣です。

三つ目はコーディングスタイルの問題です。個人的には、色々なところで使わないと無用だと思います。

最後に、正確性が心配な人のために、ここに source (Joe Duffy)を示します。これは、揮発性修飾子が単なるアトミック操作ではフィールドに必要ないことを示しています。つまり、#1と#2は同等であり、正しいので、その#3は正しいです。それでも、2年間で何か問題が発生した場合に備えて、「== 0」を「<= 0 "」に置き換えます。

0
Victor Victis