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


回答:


135

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

ロック(obj)

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

モニター

  • lock(obj)は、モニターを使用して内部的に実装されます。クリーンアップ手順を忘れるような間抜けを防ぐため、lock(obj)を選択する必要があります。可能であれば、それは「ばかばかしい」モニター構造です。
    モニターは.NET Framework用に特別に設計されており、リソースをより有効に利用できるため、一般に、モニターの使用はミューテックスよりも優先されます。

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

ミューテックス:

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

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

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

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


18
あなたMonitorはコミュニケーションを許可しないと主張しています。することができますそれでもPulseなどとMonitor
マルクGravell

3
セマフォの代替の説明を確認してください-stackoverflow.com/a/40473/968003。セマフォをナイトクラブの用心棒と考えてください。一度にクラブに入会できる献身的な人数がいます。クラブが満員の場合、誰も入場できませんが、1人が去るとすぐに別の人が入場する可能性があります。
Alex Klaus、

31

「その他の.Net同期クラスの使用」について-あなたが知っておくべきその他のいくつか:

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

また、CCR / TPL(Parallel Extensions CTP)にはより多くの(オーバーヘッドの少ない)ロック構造がありますが、IIRCは.NET 4.0で利用できるようになります。


したがって、単純な信号通信が必要な場合(非同期操作の完了など)-Monitor.Pulseを実行する必要がありますか?またはSemaphoreSlimまたはTaskCompletionSourceを使用しますか?
Vivek 2014年

非同期操作にはTaskCompletionSourceを使用します。基本的には、スレッドについて考えるのをやめて、タスク(作業単位)について考えるようにします。スレッドは実装の詳細であり、関連性はありません。TCSを返すことで、結果、エラーを返したり、キャンセルを処理したりできます。また、TCSを他の非同期操作(async awaitやContinueWithなど)と簡単に組み合わせることができます。
Simon Gillbee、2015

14

ECMAで述べられているように、Reflectedメソッドから確認できるように、lockステートメントは基本的に以下と同等です。

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

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

Mutexe は文字列識別子をロックできるため、プロセス間同期が必要な場合に役立ちます。異なるプロセスが同じ文字列識別子を使用して、ロックを取得できます。

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


5
この糖衣構文が少し出てC#4チェックに変更されましたblogs.msdn.com/ericlippert/archive/2009/03/06/...
ピーターGfader

14

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

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

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

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

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

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

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

MonoとDotGNUは、ミューテックスを効果的に使用してロック/待機を実行しますが、スピンロックスタイルの比較および交換操作を使用して、本当に必要でない限り、実際にハードロックを実行する必要をなくします。

モニターを実装する方法の例を以下に示します。

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


9

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

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


5

可能であれば、「lock()」、「Mutex」、「Monitor」を回避しようとします...

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

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

ConcurrentDictionaryロック!もう手動ロックは不要です!


2
ロックを回避してモニターを使用しますか?どうして?
mafu

@mafutrct同期を自分で処理する必要があるため。
Peter Gfader、2011年

ああ、私はそれを理解しました、あなたは言及された3つのアイデアのすべてを避けることを意味しました。モニターは使用するが、ロック/ミューテックスは使用しないようです。
mafu 2011年

System.Collections.Concurrentを使用しないでください。それらは競合状態の主な原因であり、呼び出し元のスレッドもブロックします。
アレクサンダーダニロフ

-2

ほとんどの場合、ロック(=モニター)やミューテックス/セマフォ使用しないでください。それらはすべて現在のスレッドをブロックします。

また、クラスは絶対に使用 System.Collections.Concurrentしないでください。複数のコレクション間のトランザクションをサポートせず、現在のスレッドをブロックしないため、クラスは競合状態の主な原因になります。

驚いたことに、.NETには同期のための効果的なメカニズムがありません。

C#にGCD(world)からのシリアルキューを実装しましたObjc/Swift-非常に軽量で、スレッドプールを使用する同期ツールをブロックせず、テストを行います。

ほとんどの場合、データベースアクセス(hello sqlite)からビジネスロジックまで、あらゆるものを同期するための最良の方法です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.