マルチスレッド処理のためのデータの同期
複数のスレッドが同じオブジェクトのプロパティとメソッドを呼び出す場合は、これらの呼び出しを同期することが重要です。 同期しないと、1 つのスレッドが行っていることを別のスレッドが中断し、オブジェクトが無効な状態になってしまう可能性があります。 メンバーがこのように中断されないように保護されているクラスを、スレッドセーフと呼びます。
.NET には、インスタンスや静的メンバーへのアクセスを同期するためのいくつかの方法が用意されています。
同期されたコード領域。 Monitor クラス、またはこのクラスに対するコンパイラ サポートを使用して、パフォーマンスを向上させながら、同期を必要とするコード ブロックだけを同期できます。
手動での同期。 .NET クラス ライブラリによって提供されている同期オブジェクトを使用できます。 「同期プリミティブの概要」を参照してください。これには、Monitor クラスの説明が含まれています。
同期されたコンテキスト。 .NET Framework および Xamarin アプリケーションでのみ、SynchronizationAttribute を使用することで、ContextBoundObject オブジェクトの単純な自動同期を有効にすることができます。
System.Collections.Concurrent 名前空間のコレクション クラス。 これらのクラスには、同期された追加操作および削除操作が組み込まれています。 詳しくは、「スレッド セーフなコレクション」を参照してください。
共通言語ランタイムにはスレッド モデルが用意されていて、要件に応じたさまざまな方法で同期することができる多数のカテゴリにクラスを分類できます。 次の表に、各同期カテゴリで提供されるフィールドおよびメソッドに対する同期サポートを示します。
カテゴリ | グローバル フィールド | 静的フィールド | 静的メソッド | インスタンス フィールド | インスタンス メソッド | 特定のコード ブロック |
---|---|---|---|---|---|---|
同期なし | いいえ | 番号 | 番号 | 番号 | 番号 | いいえ |
同期されたコンテキスト | いいえ | 番号 | 番号 | イエス | はい | いいえ |
同期されたコード領域 | いいえ | いいえ | マークされている場合にのみ | いいえ | マークされている場合にのみ | マークされている場合にのみ |
手動での同期 | 手動 | 手動 | 手動 | 手動 | 手動 | 手動 |
同期なし
これは、オブジェクトに対する既定の設定です。 すべてのスレッドが、すべてのメソッドまたはフィールドにいつでもアクセスできます。 ただし、これらのオブジェクトにアクセスできるスレッドは一度に 1 つだけです。
手動での同期
.NET クラス ライブラリには、スレッドを同期するための多数のクラスがあります。 「同期プリミティブの概要」を参照してください。
同期されたコード領域
Monitor クラスまたはコンパイラ キーワードを使用して、コード ブロック、インスタンス メソッド、静的メソッドを同期できます。 同期された静的フィールドに対するサポートはありません。
Visual Basic と C# の両方が、コード ブロックに特定の言語キーワード (C# の lock
ステートメント、Visual Basic の SyncLock
ステートメント) のマークを付けることをサポートしています。 スレッドによってコードが実行されると、ロックの取得が試行されます。 別のスレッドによってロックが既に取得されている場合、ロックが使用可能になるまでスレッドはブロックされます。 同期されているコード ブロック部分の実行をスレッドが終了すると、終了方法に関係なく、ロックが解放されます。
Note
C# 13 以降、lock
ステートメントは、 ロックされたオブジェクトが System.Threading.Lock のインスタンスであるかどうかを認識し、EnterScope
メソッドを使用して同期領域を作成します。 ターゲットが Lock
インスタンスでない場合の 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 ハンドラーが実行され、任意のクリーンアップ作業を行えるようになります。
同期されたコンテキスト
.NET Framework と Xamarin のアプリケーションでのみ、任意の ContextBoundObject で SynchronizationAttribute を使用して、すべてのインスタンス メソッドとフィールドを同期できます。 同じコンテキスト ドメイン内のすべてのオブジェクトが同じロックを共有します。 複数のスレッドがメソッドやフィールドにアクセスできますが、これらのオブジェクトに一度にアクセスできるのは 1 つのスレッドだけです。
関連項目
.NET