ロックはどのように正確に機能しますか?


527

スレッドセーフではないオブジェクトを使用する場合、次のようなロックでコードをラップすることがわかります。

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

したがって、複数のスレッドが同じコードにアクセスするとどうなりますか(ASP.NET Webアプリケーションで実行されていると仮定しましょう)。彼らはキューに入っていますか?もしそうなら、彼らはどれくらい待つでしょうか?

ロックを使用することによるパフォーマンスへの影響は何ですか?


1
^デッドリンク、参照:jonskeet.uk/csharp/threads/index.html
IvanPavičić19年

回答:


448

lock文は以下にC#3.0で翻訳されています:

var temp = obj;

Monitor.Enter(temp);

try
{
    // body
}
finally
{
    Monitor.Exit(temp);
}

C#4.0ではこれが変更され、次のように生成されます。

bool lockWasTaken = false;
var temp = obj;
try
{
    Monitor.Enter(temp, ref lockWasTaken);
    // body
}
finally
{
    if (lockWasTaken)
    {
        Monitor.Exit(temp); 
    }
}

ここでMonitor.Enterが行われるかについての詳細情報を見つけることができます。MSDNを引用するには:

Enterパラメータとして渡されたオブジェクトのモニターを取得するために使用します。別のスレッドがEnter オブジェクトに対してを実行したが、対応するをまだ実行していないExit場合、現在のスレッドは、他のスレッドがオブジェクトを解放するまでブロックします。同じスレッドがEnterブロックせずに複数回呼び出すことは正当です 。ただし、Exitオブジェクトを待機している他のスレッドのブロックが解除される前に、同数の 呼び出しを呼び出す必要があります。

Monitor.Enter方法は無限に待機します。それはなりませんタイムアウト。


15
MSDNによると、「ロック(C#)またはSyncLock(Visual Basic)キーワードを使用することは、一般に直接Monitorクラスを使用するよりも優先されます。保護されたコードが例外をスローした場合。これは、例外がスローされたかどうかに関係なく、関連するコードブロックを実行するfinallyキーワードで実行されます。 msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden

10
var temp = obj;のポイントは何ですか?ライン。そもそもそれは参考に過ぎないので、別のものを作ることは何がいいのですか?
priehl 2014

11
@priehlこれにより、objシステム全体をデッドロックせずにユーザーが変更できます。
スティーブン

7
@Joymonは、結局のところ、すべての言語機能は構文糖です。言語機能とは、ロック機能と同様に、開発者の生産性を高め、アプリケーションの保守性を高めることです。
Steven

2
正しい。これがlock-statementとMonitorの目的です。そのため、あるスレッドで操作を実行しても、別のスレッドが操作を心配する必要はありません。
ディジーH.マフィン2017年

285

思ったより簡単です。

Microsoftによれば、このlockキーワードは、あるスレッドがコードのクリティカルセクションに入らず、別のスレッドがクリティカルセクションに入っていることを保証します。別のスレッドがロックされたコードを入力しようとすると、オブジェクトが解放されるまで待機、ブロックされます。

lockキーワードのコールEnterブロックの開始時とExitブロックの終わりに。lockキーワードは実際にはMonitorクラスをバックエンドで処理します。

例えば:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

上記のコードでは、スレッドは最初にクリティカルセクションに入り、次にロックしますobj。別のスレッドが開始しようとするとobj、最初のスレッドによって既にロックされているもロックしようとします。2番目のスレッドは、最初のスレッドが解放されるのを待つ必要がありますobj。最初のスレッドが終了すると、別のスレッドがロックobjされ、クリティカルセクションに入ります。


9
ロックするダミーオブジェクトを作成する必要がありますか、それともコンテキスト内の既存の変数をロックできますか?
batmaci

9
@batmaci-別のプライベートダミーオブジェクトをロックすると、そのオブジェクトで他の誰もロックしていないことが保証されます。データをロックし、同じデータが外部に見える場合、その保証は失われます。
Umar Abbas

8
ロックが解除されるのを複数のプロセスが待っている場合はどうなりますか?待機中のプロセスは、クリティカルセクションをFIFO順にロックするようにキューに入れられていますか?
jstuardo 2017

@jstuardo-キューに入れられますが、順序がFIFOであるとは限りません。このリンクを確認してください:albahari.com/threading/part2.aspx
Umar Abbas


47

いいえ、彼らはキューに入っていません、彼らは寝ています

次の形式のロックステートメント

lock (x) ... 

ここで、xは参照型の式であり、正確に以下と同等です。

var temp = x;
System.Threading.Monitor.Enter(temp); 
try { ... } 
finally { System.Threading.Monitor.Exit(temp); }

あなたは彼らがお互いに待っていることを知る必要があるだけで、1つのスレッドだけがブロックをロックするために入り、他のスレッドは待つでしょう...

モニターは.netで完全に記述されているので十分高速です。詳細については、リフレクター付きのクラスモニターも ご覧ください。


6
注のために放出されたコードというlock文は、C#4で若干変更:blogs.msdn.com/b/ericlippert/archive/2009/03/06/...
LukeH

@ArsenMkrt、それらは「ブロックされた」状態「キュー」に保持されていません。スリープ状態とブロック状態にはいくつかの違いがあると思いますよね?
Mohanavel

@Mohanavelどういう意味ですか?
Arsen Mkrtchyan 2014年

1
それは問題ではありませんでした。質問は「ロック」キーワードに関するものでした。プロセスが「ロック」セクションに入るとします。つまり、プロセスはそのコードをブロックし、他のプロセスはそのロックが解除されるまでそのセクションに入ることができない可能性があります。さて....今、さらに2つのプロセスが同じブロックに入ろうとします。「lock」キーワードで保護されているため、このフォーラムでの発言によると、彼らは待機します。最初のプロセスがロックを解放したとき。どのプロセスがブロックに入りますか?入力しようとした最初のものか、最後のものですか?
jstuardo 2017

1
答えはノーであるよりも、1が入ります保証はありませんので、場合、私はもっとここに... ...あなたの代わりに、プロセスの平均スレッドを推測stackoverflow.com/questions/4228864/...
アルセンMkrtchyan

29

ロックは、他のスレッドがロックブロックに含まれるコードを実行するのをブロックします。スレッドは、ロックブロック内のスレッドが完了してロックが解放されるまで待機する必要があります。これは、マルチスレッド環境でのパフォーマンスに悪影響を及ぼします。これを行う必要がある場合は、ロックブロック内のコードが非常に迅速に処理できることを確認する必要があります。データベースへのアクセスなどの高額な作業は避けてください。


11

パフォーマンスへの影響は、ロックする方法によって異なります。ここで最適化の良いリストを見つけることができます:http : //www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

待機中のコードがスリープ状態になるため、基本的にロックはできるだけ少なくする必要があります。重い計算や、長時間続くコード(ファイルのアップロードなど)がロックされていると、パフォーマンスが大幅に低下します。


1
しかし、ローロックコードを書き込もうとすると、たとえその分野の専門家であっても、微妙で発見および修正が難しいバグが発生する可能性があります。ロックを使用することは、多くの場合、2つの悪のうちの小さい方です。必要なだけロックする必要があります。それ以上でもそれ以下でもありません!
LukeH、

1
@LukeH:ローロックコードが非常に単純で簡単な使用パターンがいくつかあります[ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue]。最大の危険は、そのような手法で処理できる範囲を超えて要件が進化すると、それを処理するようにコードを適合させることが困難になる可能性があることです。
スーパーキャット2013年

リンクが壊れています。
CarenRose

8

ロックステートメント内の部分は1つのスレッドでのみ実行できるため、他のすべてのスレッドは、ロックを保持しているスレッドが終了するまで無期限に待機します。これにより、いわゆるデッドロックが発生する可能性があります。


8

lock文はへの呼び出しに変換されるEnterExitする方法Monitor

lockロックオブジェクトが解放されるのステートメントは、無期限に待機します。


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