2

を使用ReaderWriterLockSlimして読み取り/書き込みロックを取得する場合、変数を作成する必要がありvolatileますかInterlocked.Increment?

たとえば、Add以下のメソッドのコードは正常に機能しますか?それとも拡張が必要ですか?

public class AppendableList<T> { // semi-immutable; supports appending only
    private T[] data = new T[16];
    private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
    public int Count { get; private set; }
    public T this[int index] {
        get {
            rwLock.EnterReadLock();
            try { return data[index]; } finally { rwLock.ExitReadLock(); }
        }
    }
    public void Add(T item) {
        rwLock.EnterUpgradeableReadLock();
        try {
            if (Count == data.Length)
                reAllocateArray(); // upgrades to write lock
            data[Count++] = item; // do I need to use Interlocked here?
        } finally { rwLock.ExitUpgradeableReadLock(); }
    }
}

編集:複数のスレッドがデータに同時にアクセスできるようにする軽量で高速でシンプルなリストを作成しようとしています(一種のプロデューサー/コンシューマーバッファー)。上記のコードを編集して、以前に使用した簡略化を削除したので、問題がより明確になるはずです。このコードはスレッドセーフであるように思えますがCount、アップグレード可能なロックを終了した直後にすべてのスレッドが の更新された値を見るかどうかはわかりません。

編集 2 : ここでの「書き込み」ロックは、配列要素ではなく、配列参照への書き込みを示すために使用されます。これで十分だと思います (データ自体は不変であるため)。インクリメントするときは Interlocked を使用する必要があると思いますCount。本当?

4

2 に答える 2

3

書き込みロックが (特に書き込みロック内で) メモリ バリアとして機能することを十分に期待していますが、それを直接証明することはできません。

の複雑さが必要かどうかはReaderWriterLockSlim、コンテキストによって異なります。Interlockedvolatilelockまたは[MethodImpl]それぞれがはるかに簡単に仕事をするかもしれません。主にReaderWriterLock[Slim]、リーダーが多く、ライターが少ない場合に必要です。

ただし、getは現在ロックによって保護されていません。複数の操作が書き込みロックによってスパンされる必要がある場合は、明示的なプロパティの実装と読み取りロックを自分で取得する必要があります (リーダーが中間値を表示することなく)。

余談ですが、Count++使用法はおそらく人々にとってより親しみやすいでしょう。

try/finallyを使用して、例外のロックを確実に解放する必要もあります。

書き込みロックと読み取りロックの問題を回避するには、おそらく次のようにします。

    private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();

    private int count;
    public int Count {
        get {
            rwLock.EnterReadLock();
            int tmp = count;
            rwLock.ExitReadLock();
            return tmp;
        }
    }
    public void Add(object x) {
        rwLock.EnterWriteLock();
        try {
            // do some processing
            count++;
        } finally {
            rwLock.ExitWriteLock();
        }
    }

あなたの編集を更新しました。

それはかなり堅実に見えます。Aは、すべての倍増などを内部で実行し、多くのコードを節約できるため、List<T>(配列よりも) 私の推奨事項です。T[]一度に更新できるのは 1 つのスレッドだけなのでCount、 の必要はありません。別のスレッドが行を追加している間に呼び出し元が古いものを取得しても問題ない限り、Interlockedこのプロパティは を読み取るときにロックする必要がありません。ブロックされています)。CountCount

于 2008-12-20T15:34:56.393 に答える
1

はい、そうです。さまざまなメモリバリアケースの非常に詳細な概要については、そのドキュメントをチェックしてください。必要に応じて、フェンスのないロックも見つけることができます。

そして、揮発性のものは使用しないでください。最近ではますます効果が低下しています!!

すべての標準的な Windows ロック メカニズム (スピン ロック、ミューテックス、カーネル イベント、エグゼクティブ リソース パッケージによって管理されるリソース) は、実行可能コードで必要な場所にメモリ バリアを挿入することで、プロセッサの並べ替えから保護します。

メモリ バリアは、他のプロセッサから見た読み取りおよび/または書き込み操作の順序を保持するプロセッサ命令です。メモリ バリアには、取得、解放、およびフェンス セマンティクスを含むプロセッサ命令が含まれます。これらのセマンティクスは、操作の結果が表示される順序を記述します。

  • 取得セマンティクスとは、操作の結果が、コード内でその後に表示される操作の結果よりも前に表示されることを意味します。
  • リリース セマンティクスとは、操作の結果がコード内で前に表示される操作の結果の後に表示されることを意味します。
  • フェンス セマンティクスは、取得セマンティクスと解放セマンティクスを組み合わせたものです。フェンス セマンティクスを使用した操作の結果は、コード内でその後に表示される操作の結果よりも前に表示され、その前に表示される操作の結果よりも後に表示されます。
于 2009-05-29T11:02:35.757 に答える