使用Threading.Interlocked.Increment
は、ロックを取得し、インクリメントを実行し、ロックを解放するよりも少し速くなりますが、それほど速くはありません。マルチコアシステムでのいずれかの操作のコストのかかる部分は、コア間のメモリキャッシュの同期を強制することです。の主な利点Interlocked.Increment
は速度ではなく、制限された時間内に完了するという事実です。対照的に、ロックを取得し、インクリメントを実行し、ロックを解放しようとすると、カウンターを保護する以外の目的でロックが使用されたとしても、他のスレッドがあれば永遠に待たなければならないリスクがあります。ロックを取得してから、ウェイレイドします。
Concurrent
使用している.netのバージョンについては言及していませんが、役立つ可能性のあるクラスがいくつかあります。物を割り当てたり解放したりするパターンによっては、少しトリッキーに見えるかもしれませんが、うまく機能する可能性のあるクラスは、ConcurrentBag
クラス。キューやスタックに似ていますが、特定の順序で処理が行われる保証はありません。リソースラッパーに、それがまだ良好かどうかを示すフラグを含め、リソース自体にラッパーへの参照を含めます。リソースユーザーが作成されたら、ラッパーオブジェクトをバッグに入れます。リソースユーザーが不要になったら、「無効」フラグを設定します。「有効」フラグが設定されているラッパーオブジェクトがバッグ内に少なくとも1つあるか、リソース自体が有効なラッパーへの参照を保持している限り、リソースは存続している必要があります。アイテムが削除されたときにリソースが有効なラッパーを保持していないように見える場合は、ロックを取得し、リソースがまだ有効なラッパーを保持していない場合は、有効なラッパーが見つかるまでラッパーをバッグから引き出します。次に、そのリソースをリソースとともに保存します(または、リソースが見つからない場合は、リソースを破棄します)。アイテムが削除されたときにリソースが有効なラッパーを保持しているが、バッグが無効なアイテムを過剰に保持しているように見える場合は、ロックを取得し、バッグの内容を配列にコピーして、有効なアイテムをバッグに戻します。スローされたアイテムの数を数えておくと、次のパージをいつ実行するかを判断できます。
このアプローチは、ロックやを使用するよりも複雑に見えるThreading.Interlocked.Increment
場合があり、心配する必要のあるコーナーケースがたくさんありますが、ConcurrentBag
リソースの競合を減らすように設計されているため、パフォーマンスが向上する可能性があります。プロセッサ1が実行する場合Interlocked.Increment
ある場所で、プロセッサ2がそうすると、プロセッサ2はプロセッサ1にその場所をキャッシュからフラッシュするように指示し、プロセッサ1がそうするまで待って、他のすべてのプロセッサにその場所の制御が必要であることを通知し、その場所をキャッシュに入れ、最後にそれをインクリメントすることに取り掛かります。それがすべて起こった後、プロセッサ1が場所を再度インクリメントする必要がある場合は、同じ一般的な手順のシーケンスが必要になります。これはすべて非常に遅いです。対照的に、ConcurrentBagクラスは、複数のプロセッサがキャッシュの衝突なしにリストに物を追加できるように設計されています。物事が追加されてから削除されるまでの間に、一貫性のあるデータ構造にコピーする必要がありますが、そのような操作は、優れたキャッシュパフォーマンスが得られるようにバッチで実行できます。
を使用して上記のようなアプローチを試したことがないConcurrentBag
ため、実際にどのようなパフォーマンスが得られるかはわかりませんが、使用パターンによっては、参照カウントよりも優れたパフォーマンスが得られる場合があります。