1

私はジェフリー・リヒターのビデオを見ました(正確な行を見るにはクリックしてください)、そして彼は言います:

Monitor.Enter と Monitor.Lock は Event-wait-handles や Semaphore などよりも (monitor.X)カーネル オブジェクトを使用するため、常に使用することをお勧めしますが、競合がある場合にのみ使用します。競合がなければ、それらのオブジェクトは使用されません。

ここで何かが足りないかもしれませんが、そうすると:

lock(myObj)
{
 ...
}

クリティカルセクションに入りたいスレッドが複数存在する可能性があると思います。
では、上記の情報によると、競合がなければロックは使用されないのでしょうか? (別のスレッドが 1 ミリ秒後に入力しようとしている場合はどうなりますか?)

4

3 に答える 3

3

はい、Monitor クラスは CLR で非常にうまく最適化されています。他のスレッドがまだモニターを所有していない場合、それは非常に安価です。追加のストレージに料金を支払う必要さえありません。ロック状態は、オブジェクト ヘッダーの一部である、すべてのオブジェクトが既に使用可能な Object のフィールドに格納されます。

Monitor.Enter() メソッドは、ロックが同じスレッドによって既に所有されているかどうかを最初にチェックすることで、OS コードへの入力を回避します。これにより、再入可能になります。その場合は、単にロック カウントをインクリメントします。次に、どのプロセッサでも非常に安価なプリミティブである Interlocked.CompareExchange() と同等のものを使用して、ロックの取得を試みます。x86 バージョンは、実際にバス ロックをまったく取得しないことで注目に値します。この回答でそのコードを確認できます。

それが機能しない場合は、オペレーティング システムが関与する必要があります。ロックが解放されたときに、スレッド コンテキスト スイッチを作成してスレッドを元に戻すことができることが重要だからです。Windows は、OS 同期オブジェクトを待機していたスレッドを選択することを強く推奨します。これにより、可能な限り迅速に再び実行を開始できるようになります。基礎となるオブジェクトは単純なイベントであり、非常に安価な OS オブジェクトです。また、公平性も考慮し、待機中のスレッドはキューに入れられ、先入れ先出しの順序で解放されます。この回答で、基になる CLR コードを文書化しました。

于 2013-03-16T12:43:47.257 に答える
2

では、上記の情報によると、競合がなければロックは使用されないのでしょうか? (別のスレッドが 1 ミリ秒後に入力しようとしている場合はどうなりますか?)

正しい。次に、競合が発生し、他のスレッドがカーネルに入る必要があります。また、ロックを保持しているスレッドも、ロックを解除するときにカーネルに入る必要があります。

操作は次のようになります。

ロック:

  1. ユーザー空間のロック変数をアンロックからロックにアトミックに設定してみてください。もしそうなら、やめて、終わりです。

  2. ユーザー空間の競合カウントを増やします。

  3. カーネル空間ロックをロック済みに設定します。

  4. ユーザー空間のロック変数をアンロックからロックにアトミックに設定してみてください。その場合、ユーザー空間の競合カウントを減らして停止すれば完了です。

  5. カーネル ロックでカーネルをブロックします。

  6. 手順 3 に進みます。

ロック解除:

  1. ユーザー空間のロック変数をロックからロック解除にアトミックに設定します。

  2. ユーザー空間の競合カウントがゼロの場合は、停止してください。

  3. カーネル ロックをロック解除に設定します。

競合がない場合、ロック操作にはステップ 1 のみが含まれ、ロック解除操作にはステップ 1 と 2 のみが含まれることに注意してください。これらはすべてユーザー空間で行われます。

于 2013-03-16T11:37:20.417 に答える
2

このlockステートメントはMonitor.Enter、 andMonitor.Exitをその実装として使用する単なる構文糖衣です。

Monitor関数自体は、条件変数を使用して実装されます。それらの実装は、実際にロックの競合がない限り、カーネル オブジェクトを割り当てる必要がないことを意味します。その場合、先に進んでカーネル オブジェクトを割り当てる必要があります。

ロックの競合があっても、すぐにカーネル オブジェクトを割り当てません。代わりに、ロックが解放されることを期待して、「スピン」します (短い間、タイトなループに座っているだけです)。解放されない場合にのみ、カーネル オブジェクトの割り当て/使用を続けます。

ManualResetEventSlimなどの新しい同期クラスの一部も、このアプローチを採用していることに注意してください。(一般に、最後に「Slim」を含むすべての同期クラスがこれを行います。)

このスレッドも参照してください。

についての質問に直接答えるにはlock: はい、競合がない場合、または競合が非常に短時間しか続かない場合、カーネル オブジェクトを使用するためにカーネル モードに移行することはありません。競合が短時間以上続く場合にのみ、カーネル モードへの移行が発生します。

于 2013-03-16T10:58:10.093 に答える