web-dev-qa-db-ja.com

C#のさまざまなスレッド同期オプションの違いは何ですか?

誰かが以下の違いを説明できますか?

  • ロック(someobject){}
  • Mutexの使用
  • セマフォの使用
  • モニターを使用する
  • 他の.Net同期クラスの使用

わかりません。私には最初の2つは同じようですか?

158
user38834

いい質問ですね。私は間違っているかもしれません..試してみましょう..私のオリジナルの回答のリビジョン#..少し理解を深めてください。読んでくれてありがとう:)

lock(obj)

  • (オブジェクト内?)スレッド同期のためのCLRコンストラクトです。 1つのスレッドのみがオブジェクトのロックの所有権を取得し、ロックされたコードブロックを入力できるようにします。他のスレッドは、現在の所有者がコードブロックを終了してロックを放棄するまで待機する必要があります。また、クラスのプライベートメンバーオブジェクトをロックすることをお勧めします。

Monitors

  • lock(obj)は、モニターを使用して内部的に実装されます。 lock(obj)を使用することをお勧めします。必要に応じて、Monitorコンストラクトは「ばか防止」されます。
    モニターは、.NET Framework用に特別に設計されたため、リソースをより有効に使用できるため、一般的にミューテックスよりもモニターの使用が推奨されます。

ロックまたはモニターの使用は、スレッドの影響を受けやすいコードブロックの同時実行を防ぐのに役立ちますが、これらの構造では、あるスレッドが別のスレッドとイベントをやり取りすることはできません。これには、同期イベントが必要です。これは、スレッドのアクティブ化と一時停止に使用できる、シグナル状態と非シグナル状態の2つの状態のいずれかを持つオブジェクトです。ミューテックス、セマフォはOSレベルの概念です。たとえば、名前付きミューテックスを使用すると、複数の(管理された)exe間で同期できます(アプリケーションの1つのインスタンスのみがマシンで実行されていることを確認します)。

Mutex:

  • ただし、モニターとは異なり、a mutexはプロセス間でスレッドを同期するために使用できます。プロセス間同期に使用される場合、mutexはnamed mutexと呼ばれます。別のアプリケーションで使用されるため、グローバル変数または静的変数を使用して共有することはできません。両方のアプリケーションが同じmutexオブジェクトにアクセスできるように、名前を付ける必要があります。対照的に、MutexクラスはWin32コンストラクトのラッパーです。ミューテックスはモニターよりも強力ですが、相互運用の遷移が必要です。これは、Monitorクラスで必要なものよりも計算コストが高くなります。

セマフォ(私の脳を傷つけます)。

  • Semaphoreクラスを使用して、リソースのプールへのアクセスを制御します。スレッドは、WaitHandleクラスから継承されたWaitOneメソッドを呼び出してセマフォに入り、Releaseメソッドを呼び出してセマフォを解放します。セマフォのカウントは、スレッドがセマフォに入るたびに減分され、スレッドがセマフォを解放するたびに増分されます。カウントがゼロの場合、他のスレッドがセマフォを解放するまで、後続の要求はブロックされます。すべてのスレッドがセマフォを解放すると、カウントはセマフォの作成時に指定された最大値になります。スレッドはセマフォに複数回入ることができます。Semaphoreクラスは、WaitOneまたはReleaseでスレッドIDを強制しません。プログラマーがマックアップしない責任を負います。システムセマフォ。名前を受け入れるコンストラクタを使用してセマフォオブジェクトを作成すると、その名前のオペレーティングシステムセマフォに関連付けられます。名前付きシステムセマフォはオペレーティングシステム全体で表示され、アクティビティの同期に使用できます。プロセスの。ローカルセマフォはプロセス内にのみ存在します。ローカルのセマフォオブジェクトへの参照を持つプロセス内の任意のスレッドで使用できます。各セマフォオブジェクトは、個別のローカルセマフォです。

読むページ-スレッドの同期(C#)

131
Gishu

「他の.Net同期クラスの使用」について-知っておく必要のある他のいくつか:

  • ReaderWriterLock -複数のリーダーまたは単一のライターを許可します(同時にではありません)
  • ReaderWriterLockSlim -上記のように、オーバーヘッドを削減
  • ManualResetEvent -開いているときにコードを通過させるゲート
  • AutoResetEvent -上記と同じですが、一度開くと自動的にシャットダウンします

CCR/TPL( Parallel Extensions CTP)にはさらに多くの(低オーバーヘッド)ロック構造もありますが、IIRC、これらは.NET 4.0で利用可能になります

29
Marc Gravell

ECMAで述べられているように、またReflectedメソッドからわかるように、lockステートメントは基本的に次と同等です。

object obj = x;
System.Threading.Monitor.Enter(obj);
try {
   …
}
finally {
   System.Threading.Monitor.Exit(obj);
}

前述の例から、モニターはオブジェクトをロックできることがわかります。

ミューテックスは、プロセス間同期が必要なときに便利ですcan文字列識別子をロックします。同じ文字列識別子を異なるプロセスで使用して、ロックを取得できます。

セマフォはステロイドのミューテックスに似ており、同時アクセスの最大数を提供することで同時アクセスを許可します。制限に達すると、呼び出し元の1人がセマフォを解放するまで、セマフォはリソースへのそれ以上のアクセスのブロックを開始します。

14
arul

DotGNUでスレッド化のクラスとCLRをサポートしましたが、いくつかの考えがあります...

プロセスにまたがるロックを必要としない限り、ミューテックスとセマフォの使用は常に避けてください。 .NETのこれらのクラスは、Win32 MutexおよびSemaphoresのラッパーであり、かなり重いです(特に、ロックが競合していない場合は、カーネルへのコンテキストスイッチが必要です)。

他にも言及されているように、C#lockステートメントはMonitor.EnterおよびMonitor.Exitのコンパイラーマジックです(try/finally内に存在します)。

モニターには、Monitor.Pulse/Monitor.Waitメソッドを介したミューテックスにはないシンプルで強力な信号/待機メカニズムがあります。 Win32に相当するのは、CreateEventを介したイベントオブジェクトで、実際には.NETにもWaitHandlesとして存在します。 Pulse/Waitモデルは、Unixのpthread_signalおよびpthread_waitに似ていますが、競合しない場合は完全にユーザーモード操作にできるため、高速です。

Monitor.Pulse/Waitは簡単に使用できます。 1つのスレッドで、オブジェクトをロックし、フラグ/状態/プロパティをチェックします。それが予期したものでない場合は、Monitor.Waitを呼び出してロックを解除し、Pulseが送信されるまで待機します。待機が戻ると、ループバックしてフラグ/状態/プロパティを再度チェックします。もう1つのスレッドでは、フラグ/状態/プロパティを変更するたびにオブジェクトをロックし、PulseAllを呼び出してリスニングスレッドを起動します。

多くの場合、クラスをスレッドセーフにして、コードにロックを設定します。ただし、多くの場合、クラスは1つのスレッドでのみ使用されます。これは、ロックによってコードが不必要に遅くなることを意味します...これは、CLRの巧妙な最適化がパフォーマンスの向上に役立つ場所です。

Microsoftのロックの実装についてはわかりませんが、DotGNUとMonoでは、ロック状態フラグがすべてのオブジェクトのヘッダーに保存されます。 .NET(およびJava)のすべてのオブジェクトはロックになる可能性があるため、すべてのオブジェクトはヘッダーでこれをサポートする必要があります。 DotGNU実装には、ロックとして使用されるすべてのオブジェクトに対してグローバルハッシュテーブルを使用できるフラグがあります。これには、すべてのオブジェクトの4バイトのオーバーヘッドを排除できるという利点があります。これはメモリ(特にスレッドが多くない組み込みシステム)には適していませんが、パフォーマンスに影響があります。

MonoとDotGNUの両方は、ミューテックスを効果的に使用してロック/待機を実行しますが、スピンロックスタイル compare-and-exchange 操作を使用して、本当に必要でない限り、実際にハードロックを実行する必要性を排除します:

ここでモニターを実装する方法の例を見ることができます:

http://cvs.savannah.gnu.org/viewvc/dotgnu-pnet/pnet/engine/lib_monitor.c?revision=1.7&view=markup

13
tumtumtum

文字列IDで識別した共有Mutexのロックに関する追加の注意点は、デフォルトで「Local \」ミューテックスになり、ターミナルサーバー環境のセッション間で共有されないことです。

文字列識別子の前に「Global \」を付けて、共有システムリソースへのアクセスが適切に制御されるようにします。これに気付く前に、SYSTEMアカウントで実行されているサービスと通信を同期する際に問題が山積していました。

9
nvuono

可能な場合は、「lock()」、「Mutex」、「Monitor」を避けるようにします...

.NET 4の新しい名前空間System.Collections.Concurrentをご覧ください
いくつかのニーススレッドセーフコレクションクラスがあります

http://msdn.Microsoft.com/en-us/library/system.collections.concurrent.aspx

ConcurrentDictionaryロック!もう手動でロックする必要はありません!

5
Peter Gfader