マルチスレッド処理のためのデータの同期
更新 : 2007 年 11 月
複数のスレッドが同じオブジェクトのプロパティとメソッドを呼び出す場合は、これらの呼び出しを同期することが重要です。同期しないと、1 つのスレッドが行っていることを別のスレッドが中断し、オブジェクトが無効な状態になってしまうことがあります。メンバがこのような干渉を受けないように保護されているクラスを、スレッド セーフと呼びます。
共通言語基盤には、インスタンスと静的メンバへのアクセスを同期するいくつかの方法が用意されています。
同期されたコード領域。Monitor クラス、またはこのクラスのコンパイラ サポートを使用して、パフォーマンスを向上させながら、同期を必要とするコード ブロックだけを同期できます。
手動同期。.NET Framework クラス ライブラリに用意されている同期オブジェクトを使用できます。Monitor クラスについての説明が記載されている「同期プリミティブの概要」を参照してください。
同期されたコンテキスト。SynchronizationAttribute を使用すると、ContextBoundObject オブジェクトに対して単純な自動同期を有効にできます。
Synchronized プロパティ。Hashtable や Queue など、いくつかのクラスには、クラスのインスタンスのスレッド セーフなラッパーを返す Synchronized プロパティが用意されています。「コレクションと同期 (スレッド セーフ)」を参照してください。
共通言語ランタイムにはスレッド モデルが用意されていて、要件に応じたさまざまな方法で同期することができる多数のカテゴリにクラスを分類できます。次の表で、各同期カテゴリで提供されるフィールドおよびメソッドに対する同期サポートを示します。
カテゴリ |
グローバル フィールド |
静的フィールド |
静的メソッド |
インスタンス フィールド |
インスタンス メソッド |
特定のコード ブロック |
---|---|---|---|---|---|---|
同期なし |
× |
× |
× |
× |
× |
× |
同期されたコンテキスト |
× |
× |
× |
○ |
○ |
× |
同期されたコード領域 |
× |
× |
マークされている場合のみ |
マークされている場合のみ |
||
手動同期 |
手動 |
手動 |
手動 |
手動 |
手動 |
手動 |
同期なし
これは、オブジェクトに対する既定の設定です。どのスレッドも、すべてのメソッドやフィールドにいつでもアクセスできます。ただし、これらのオブジェクトにアクセスできるスレッドは一度に 1 つだけです。
手動同期
.NET Framework クラス ライブラリには、スレッドを同期するためのさまざまなクラスが用意されています。「同期プリミティブの概要」を参照してください。
同期されたコード領域
Monitor クラスまたはコンパイラ キーワードを使用して、コード ブロック、インスタンス メソッド、および静的メソッドを同期できます。同期された静的フィールドはサポートされません。
Visual Basic および C# のいずれも、コード ブロックに特定の言語キーワードのマークを付けることをサポートしています。特定の言語キーワードとは、C# では lock ステートメント、Visual Basic では SyncLock ステートメントです。コードがスレッドによって実行されると、ロックの取得が試みられます。ロックが既に別のスレッドに取得されている場合は、ロックが使用できるようになるまで、そのスレッドがブロックされます。同期されているコード ブロック部分の実行をスレッドが終了すると、終了方法に関係なく、ロックが解放されます。
メモ : |
---|
lock ステートメントおよび SyncLock ステートメントは、Monitor.Enter および Monitor.Exit を使用して実装されるため、Monitor の他のメソッドを、同期された領域内でこれらと組み合わせて使用できます。 |
また、メソッドを MethodImplAttribute および MethodImplOptions.Synchronized で修飾することもできます。この処理は、Monitor またはメソッド全体をロックするためのコンパイラ キーワードの 1 つを使用した場合と同じ結果になります。
Thread.Interrupt を使用すると、同期されたコード領域へのアクセスの待機などのブロック操作から、スレッドを切り離すことができます。また、この Thread.Interrupt を使用することで、Thread.Sleep などの操作からスレッドを切り離すこともできます。
重要 : |
---|
static メソッド (Visual Basic では Shared メソッド) をプロテクトするために、型 (C# の typeof(MyType)、Visual Basic の GetType(MyType)、または C++ の MyType::typeid) をロックしないでください。代わりに、プライベート静的オブジェクトを使用してください。同様に、C# の this (Visual Basic の場合は Me) を使用してインスタンス メソッドをロックしないでください。代わりに、プライベート オブジェクトを使用してください。クラスまたはインスタンスは、独自のコード以外のコードでもロックできますが、デッドロックやパフォーマンス上の問題を引き起こす可能性があります。 |
コンパイラ サポート
Visual Basic と C# は、どちらも Monitor.Enter と Monitor.Exit を使用してオブジェクトをロックする言語キーワードをサポートします。Visual Basic は SyncLock ステートメントをサポートし、C# は lock ステートメントをサポートします。
どちらの場合も、コード ブロックで例外がスローされると、lock または SyncLock によって取得されたロックが自動的に解放されます。C# コンパイラおよび Visual Basic コンパイラは、try ブロックおよび finally ブロックを生成します。try ブロックは先頭に Monitor.Enter を含み、finally ブロックは Monitor.Exit を含みます。lock ブロックまたは SyncLock ブロック内で例外がスローされると、finally ハンドラが実行されて、任意のクリーンアップ処理を実行できるようになります。
同期されたコンテキスト
任意の ContextBoundObject で SynchronizationAttribut を使用して、すべてのインスタンス メソッドおよびインスタンス フィールドを同期できます。同じコンテキスト ドメイン内のすべてのオブジェクトは、同じロックを共有します。複数のスレッドがメソッドやフィールドにアクセスできますが、これらのオブジェクトに一度にアクセスできるのは 1 つのスレッドだけです。