ランチタイムの急いで答えのために編集します。
前のコードで使用されたlockステートメントは、メソッドのスコープで作成されたオブジェクトインスタンスをロックしているため、同じメソッドを呼び出す別のスレッドには影響しません。特定のコードブロックへのアクセスを同期するには、各スレッドがオブジェクトの同じインスタンスをロックできる必要があります。これを行う1つの方法(必要なセマンティクスに応じて)は、ロックオブジェクトを、それが使用されるクラスのプライベート静的変数にすることです。これにより、特定のオブジェクトの複数のインスタンスがコードブロックへのアクセスを同期できるようになります。単一の共有リソース。インスタンス固有のオブジェクトまたはリソースの個々のインスタンスに同期が必要な場合は、静的を発行する必要があります。
Volatileは、指定された変数への読み取りまたは書き込みが異なるスレッド間でアトミックになることを保証しません。これは、命令の順序を保持し、変数がレジスタ内にキャッシュされないようにするためのコンパイラのヒントです。一般に、パフォーマンスに非常に敏感なもの(ローロック/ロックフリーアルゴリズム、データ構造など)に取り組んでいる場合、または実際に行っていることがわかっている場合を除いて、Interlockedを使用することを選択します。ほとんどのアプリケーションで揮発性/インターロック/ロックを使用する場合のパフォーマンスの違いはごくわずかであるため、最も安全な保証を提供するものを使用するのが最善かどうかわからない場合は(Joe Duffyのブログと本を読んでください)。
たとえば、以下の例でvolatileを使用することはスレッドセーフではなく、インクリメントされたカウンターは10,000,000に達しません(テストを実行したときに8848450に達しました)。これは、volatileが最新の値を読み取ることのみを保証するためです(たとえば、レジスタからキャッシュされないなど)。インターロックを使用する場合、操作はスレッドセーフであり、カウンターは10,000,000に達します。
public class Incrementor
{
private volatile int count;
public int Count
{
get { return count; }
}
public void UnsafeIncrement()
{
count++;
}
public void SafeIncrement()
{
Interlocked.Increment(ref count);
}
}
[TestFixture]
public class ThreadingTest
{
private const int fiveMillion = 5000000;
private const int tenMillion = 10000000;
[Test]
public void UnsafeCountShouldNotCountToTenMillion()
{
const int iterations = fiveMillion;
Incrementor incrementor = new Incrementor();
Thread thread1 = new Thread(() => UnsafeIncrement(incrementor, iterations));
Thread thread2 = new Thread(() => UnsafeIncrement(incrementor, iterations));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Assert.AreEqual(tenMillion, incrementor.Count);
}
[Test]
public void SafeIncrementShouldCountToTenMillion()
{
const int iterations = fiveMillion;
Incrementor incrementor = new Incrementor();
Thread thread1 = new Thread(() => SafeIncrement(incrementor, iterations));
Thread thread2 = new Thread(() => SafeIncrement(incrementor, iterations));
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
Assert.AreEqual(tenMillion, incrementor.Count);
}
private void UnsafeIncrement(Incrementor incrementor, int times)
{
for (int i =0; i < times; ++i)
incrementor.UnsafeIncrement();
}
private void SafeIncrement(Incrementor incrementor, int times)
{
for (int i = 0; i < times; ++i)
incrementor.SafeIncrement();
}
}
「interlockedvolatile」を検索すると、質問に対する多くの回答が見つかります。たとえば、以下の1つは、あなたの質問に対応しています。
以下の簡単な例は
揮発性vs.連動vs.ロック