マルチスレッド用のデータの同期

複数のスレッドが 1 つのオブジェクトのプロパティとメソッドを呼び出すことができる場合、それらの呼び出しを同期することが重要です。 そうしないと、あるスレッドが別のスレッドの動作を中断し、オブジェクトが無効な状態のままになる可能性があります。 このような中断からメンバーが保護されているクラスは、スレッド セーフと呼ばれます。

.NETには、インスタンスと静的メンバーへのアクセスを同期するためのいくつかの方法が用意されています。

  • 同期されたコード領域。 このクラスの Monitor クラスまたはコンパイラ サポートを使用して、必要なコード ブロックのみを同期し、パフォーマンスを向上させることができます。

  • 手動同期。 .NET クラス ライブラリによって提供される同期オブジェクトを使用できます。 クラスの説明を含む同期Monitorを参照してください。

  • 同期されたコンテキスト。 .NET Framework アプリケーションの場合のみ、SynchronizationAttributeを使用して、ContextBoundObject オブジェクトの単純な自動同期を有効にすることができます。

  • System.Collections.Concurrent名前空間のコレクション クラス。 これらのクラスは、組み込みの同期された追加および削除操作を提供します。 詳細については、「Thread-Safe コレクション」を参照してください。

共通言語ランタイムは、クラスが要件に応じてさまざまな方法で同期できるさまざまなカテゴリに分類されるスレッド モデルを提供します。 次の表は、特定の同期カテゴリを持つフィールドとメソッドに対して提供される同期のサポートを示しています。

Category グローバル フィールド 静的フィールド 静的メソッド インスタンス フィールド インスタンス メソッド 特定のコードブロック
同期なし いいえ いいえ いいえ いいえ いいえ いいえ
同期されたコンテキスト いいえ いいえ いいえ はい はい いいえ
同期されたコード領域 いいえ いいえ マークされている場合のみ いいえ マークされている場合のみ マークされている場合のみ
手動同期 Manual Manual Manual Manual Manual Manual

同期なし

これはオブジェクトの既定値です。 任意のスレッドは、任意のメソッドまたはフィールドにいつでもアクセスできます。 これらのオブジェクトにアクセスするスレッドは一度に 1 つだけです。

手動での同期

.NET クラス ライブラリには、スレッドを同期するためのクラスが多数用意されています。 「同期プリミティブの概要」を参照してください。

同期されたコード領域

Monitor クラスまたはコンパイラ キーワードを使用して、コード、インスタンス メソッド、および静的メソッドのブロックを同期できます。 同期された静的フィールドはサポートされていません。

Visual Basicと C# の両方で、特定の言語キーワード、C# の lock ステートメント、または Visual Basic の SyncLock ステートメントを使用したコード ブロックのマーキングがサポートされています。 コードがスレッドによって実行されると、ロックの取得が試行されます。 ロックが既に別のスレッドによって取得されている場合、スレッドはロックが使用可能になるまでブロックします。 スレッドが同期されたコード ブロックを終了すると、スレッドがブロックを終了する方法に関係なく、ロックが解放されます。

Note

.NET 9 および C# 13 以降では、lock ターゲットが専用のSystem.Threading.Lock インスタンスである場合、lock ステートメントはLock.EnterScope()を使用して同期リージョンを作成します。

Visual BasicはSystem.Threading.LockでのSyncLockをサポートしていないため、専用のプライベート参照型でSyncLockを引き続き使用してください。

lockターゲットがLockインスタンスではなく、すべてのSyncLock使用に対して、コンパイラはMonitor.EnterMonitor.Exitを使用します。 その実装により、同期されたリージョンで他の Monitor メソッドを引き続き使用できます。

値が MethodImplOptions.SynchronizedMethodImplAttribute でメソッドを修飾することもできます。これは、Monitor またはコンパイラ キーワードのいずれか 1 つを使用してメソッド本体全体をロックするのと同じ効果があります。

Thread.Interrupt は、同期されたコード領域へのアクセスを待機するなどのブロック操作からスレッドを中断するために使用できます。 Thread.Interrupt は、 Thread.Sleepなどの操作からスレッドを中断するためにも使用されます。

Important

typeof(MyType) メソッド (Visual Basic のGetType(MyType) メソッド) を保護するために、型 (C# でMyType::typeid、Visual Basicでstatic、または C++ でShared) はロックしないでください。 代わりにプライベート静的オブジェクトを使用してください。 同様に、C# のthis (Visual Basic のMe) を使用してインスタンス メソッドをロックしないでください。 代わりにプライベート オブジェクトを使用してください。 クラスまたはインスタンスは、独自のコード以外のコードによってロックされ、デッドロックやパフォーマンスの問題が発生する可能性があります。

コンパイラのサポート

Visual Basicと C# の両方で、Monitor.EnterMonitor.Exitを使用してオブジェクトをロックする言語キーワードがサポートされています。 Visual Basicは SyncLock ステートメントをサポートしています。C# では、lock ステートメントがサポートされています。

どちらの場合も、コード ブロックで例外がスローされた場合、lock または SyncLock によって取得されたロックは自動的に解放されます。 C# および Visual Basic コンパイラは、try の先頭に / を、finally ブロックに Monitor.Exit を指定して tryfinally ブロックを出力します。 lock または SyncLock ブロック内で例外がスローされると、必要なクリーンアップ処理を行えるように、finally ハンドラーが実行されます。

同期されたコンテキスト

.NET Framework アプリケーションでのみ、任意のSynchronizationAttributeContextBoundObjectを使用して、すべてのインスタンス メソッドとフィールドを同期できます。 同じコンテキスト ドメイン内のすべてのオブジェクトが同じロックを共有します。 複数のスレッドがメソッドとフィールドにアクセスできますが、一度に許可されるスレッドは 1 つだけです。

こちらも参照ください