この例では、volatileキーワードを使用しても、コードはスレッドセーフになりません。volatileキーワードは通常、変数(つまり、クラスフィールド)の値を読み書きするときに、その変数の最新の値がキャッシュから読み取られるのではなく、メインメモリから読み取られるか、メインメモリに直接書き込まれるようにするために使用されます(例:たとえば、CPUレジスタ)。volatileキーワードは、「この共有フィールドでキャッシュの最適化を使用しない」という言い方であり、スレッドがフィールドのローカルコピーを使用する可能性があるため、互いの更新が表示されないという問題を取り除きます。
あなたの場合、myclassの値は実際には更新されていない(つまり、myclassを再割り当てしていない)ので、volatileは役に立ちません。また、これで実際にスレッドセーフにしたいmyclass変数の更新ではありません。とにかくケース。
実際のクラスの更新をスレッドセーフにしたい場合は、「追加」と「クリア」の周りに「ロック」を使用するのが簡単な方法です。これにより、一度に1つのスレッドのみがこれらの操作(myclassの内部状態を更新する)を実行できるようになるため、並行して実行しないでください。
ロックは次のように使用できます。
private readonly object syncObj = new object();
private readonly myclass = new myclass();
......
lock (syncObj)
{
myclass.Add(...)
}
lock (syncObj)
{
myclass.Clear(..)
}
また、サンプルコードには表示されていませんが、「追加」によって更新されている状態を読み取るコードの周囲にロックを追加する必要があります。
マルチスレッドコードを最初に作成するとき、コレクションに追加するときにロックが必要になる理由が明確でない場合があります。ListまたはArrayListを例にとると、これらのコレクションは内部的に配列をバッキングストアとして使用し、この配列を動的に「成長」させるため(つまり、新しいより大きな配列を作成し、古いコンテンツをコピーすることによって)、問題が発生します。 Addが呼び出されると、容量が満たされます。これはすべて内部で発生し、この配列と、コレクションの現在のサイズなどの変数(実際の配列の長さではなく)を維持する必要があります。したがって、内部アレイを拡張する必要がある場合、コレクションへの追加には複数のステップが含まれる場合があります。安全でない方法で複数のスレッドを使用する場合、複数のスレッドを追加すると、間接的に成長が発生する可能性があります。したがって、お互いの更新を踏みにじります。複数のスレッドが同時に追加される問題だけでなく、別のスレッドがコレクションを読み取ろうとしている可能性があるという問題もあります。内部状態が変更されている間。ロックを使用すると、他のスレッドからの干渉なしにこれらの操作が確実に実行されます。