C# でスレッド同期を行う場合、値を読み取ったり変更したりするときにオブジェクトをロックする必要がありますか?
たとえば、Queue<T> オブジェクトがあります。Enqueue と Dequeue を実行するときにロックするだけですか、それとも Count などの値をチェックするときにもロックする必要がありますか?
C# でスレッド同期を行う場合、値を読み取ったり変更したりするときにオブジェクトをロックする必要がありますか?
たとえば、Queue<T> オブジェクトがあります。Enqueue と Dequeue を実行するときにロックするだけですか、それとも Count などの値をチェックするときにもロックする必要がありますか?
MSDN から:
Queue<(Of <(T>)>) は、コレクションが変更されない限り、複数のリーダーを同時にサポートできます。それでも、コレクションの列挙は本質的にスレッドセーフな手順ではありません。列挙中のスレッド セーフを保証するために、列挙全体でコレクションをロックできます。読み取りおよび書き込みのために複数のスレッドがコレクションにアクセスできるようにするには、独自の同期を実装する必要があります。
アイテムがキューに入れられている間、リーダーがアクティブになっていないことを確認する必要があります (ロックはおそらく良い考えです)。
リフレクタのカウントを見ると、プライベート フィールドからの読み取りが明らかになります。これは、値をどうするかによっては問題ありません。これは、次のようなことを行うべきではないことを意味します (適切なロックなしで):
if(queue.Count > 0)
queue.Dequeue();
ロックで何をしたいかによって異なります。通常、この種のロックには、リーダー/ライター ロック メカニズムが必要です。
リーダー/ライターのロックとは、リーダーがロックを共有することを意味するため、複数のリーダーがコレクションを同時に読み取ることができますが、書き込むには、排他ロックを取得する必要があります。
ロックしないと、古い値を取得する可能性があります。Count を変更して書き込み操作が実行されるなどの競合状態が発生する可能性がありますが、変更前の値を取得します。たとえば、キューに項目が 1 つしかなく、スレッドが dequeue を呼び出した場合、別のスレッドがカウントを読み取り、まだ 1 であることを確認して、dequeue を再度呼び出すことがあります。2 番目の呼び出しはロックが許可されるまで実行されませんが、その時点でキューは実際には空になります。
CLR は、プロセッサの幅までの値のアトミック読み取りを保証します。したがって、32ビットで実行している場合、int
s の読み取りはアトミックになります。64 ビット マシンで実行している場合、long
s の読み取りはアトミックになります。したがって、 の場合Count
はInt32
ロックする必要はありません。
この投稿はあなたの質問に関連しています。