Wie funktioniert das Schloss genau?


527

Ich sehe, dass wir für die Verwendung von Objekten, die nicht threadsicher sind, den Code mit einer Sperre wie folgt umschließen:

private static readonly Object obj = new Object();

lock (obj)
{
    // thread unsafe code
}

Was passiert also, wenn mehrere Threads auf denselben Code zugreifen (nehmen wir an, dass er in einer ASP.NET-Webanwendung ausgeführt wird)? Sind sie in der Warteschlange? Wenn ja, wie lange werden sie warten?

Welche Auswirkungen hat die Verwendung von Sperren auf die Leistung?


Antworten:


448

Die lockAnweisung wird von C # 3.0 wie folgt übersetzt:

var temp = obj;

Monitor.Enter(temp);

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

In C # 4.0 hat sich dies geändert und wird nun wie folgt generiert:

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

Sie können weitere Informationen über das finden , was Monitor.Entertut hier . So zitieren Sie MSDN:

Verwenden Sie Enterdiese Option , um den Monitor für das als Parameter übergebene Objekt abzurufen. Wenn ein anderer Thread ein Enter Objekt ausgeführt hat, das entsprechende jedoch noch nicht ausgeführt hat Exit, wird der aktuelle Thread blockiert, bis der andere Thread das Objekt freigibt. Es ist zulässig, dass ein Thread Entermehr als einmal aufgerufen wird, ohne dass er blockiert. Es muss jedoch eine gleiche Anzahl von ExitAufrufen aufgerufen werden, bevor andere auf das Objekt wartende Threads entsperrt werden.

Die Monitor.EnterMethode wird unendlich warten; es wird keine Zeitüberschreitung geben.


15
Laut MSDN wird die Verwendung des Schlüsselworts lock (C #) oder SyncLock (Visual Basic) im Allgemeinen der direkten Verwendung der Monitor-Klasse vorgezogen, da lock oder SyncLock präziser ist und lock oder SyncLock sicherstellt, dass der zugrunde liegende Monitor sogar freigegeben wird Wenn der geschützte Code eine Ausnahme auslöst. Dies wird mit dem Schlüsselwort finally erreicht, das den zugehörigen Codeblock ausführt, unabhängig davon, ob eine Ausnahme ausgelöst wird. " msdn.microsoft.com/en-us/library/ms173179.aspx
Aiden Strydom

10
Was ist der Sinn des var temp = obj; Linie. Was nützt es, einen anderen zu machen, da es zunächst nur ein Schiedsrichter ist?
Priehl

11
@priehl Ermöglicht dem Benutzer das Ändern, objohne dass das gesamte System blockiert.
Steven

7
@Joymon schließlich ist jedes Sprachmerkmal syntaktischer Zucker. Bei Sprachfunktionen geht es darum, Entwickler produktiver zu machen und Anwendungen wartbarer zu machen, ebenso wie bei der Sperrfunktion.
Steven

2
Richtig. Dies ist der gesamte Zweck der lockAnweisung und des Monitors: Damit Sie eine Operation in einem Thread ausführen können, ohne sich Sorgen machen zu müssen, dass ein anderer Thread sie durcheinander bringt.
Dizzy H. Muffin

285

Es ist einfacher als Sie denken.

Laut Microsoft : Das lockSchlüsselwort stellt sicher, dass ein Thread keinen kritischen Codeabschnitt eingibt, während sich ein anderer Thread im kritischen Abschnitt befindet. Wenn ein anderer Thread versucht, einen gesperrten Code einzugeben, wartet er blockiert, bis das Objekt freigegeben wird.

Das lockSchlüsselwort wird Enteram Anfang des Blocks und Exitam Ende des Blocks aufgerufen. lockDas Schlüsselwort behandelt tatsächlich die MonitorKlasse am Backend.

Zum Beispiel:

private static readonly Object obj = new Object();

lock (obj)
{
    // critical section
}

Im obigen Code betritt der Thread zuerst einen kritischen Abschnitt und wird dann gesperrt obj. Wenn ein anderer Thread versucht einzutreten, versucht er auch zu sperren obj, was bereits vom ersten Thread gesperrt ist. Der zweite Thread muss warten, bis der erste Thread freigegeben ist obj. Wenn der erste Thread verlässt, wird ein anderer Thread gesperrt objund tritt in den kritischen Bereich ein.


9
sollten wir ein Dummy-Objekt zum Sperren erstellen oder können wir eine vorhandene Variable im Kontext sperren?
Batmaci

9
@batmaci - Das Sperren eines separaten privaten Dummy-Objekts gibt Ihnen die Garantie, dass niemand anderes dieses Objekt sperrt. Wenn Sie die Daten sperren und dasselbe Datenelement nach außen sichtbar ist, verlieren Sie diese Garantie.
Umar Abbas

8
Was passiert, wenn mehr als ein Prozess auf die Freigabe der Sperre wartet? Sind die Wartevorgänge in der Warteschlange, damit sie den kritischen Abschnitt in der FIFO-Reihenfolge sperren?
jstuardo

@jstuardo - Sie stehen in der Warteschlange, aber die Reihenfolge ist nicht garantiert FIFO. Schauen Sie sich diesen Link an: albahari.com/threading/part2.aspx
Umar Abbas


47

Nein, sie stehen nicht in der Warteschlange, sie schlafen

Eine Sperranweisung des Formulars

lock (x) ... 

wobei x ein Ausdruck eines Referenztyps ist, ist genau äquivalent zu

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

Sie müssen nur wissen, dass sie aufeinander warten und nur ein Thread zum Sperren des Blocks eingeht, die anderen warten ...

Monitor ist vollständig in .net geschrieben, so dass es schnell genug ist. Weitere Informationen finden Sie in der Klasse Monitor mit Reflektor


6
Beachten Sie, dass sich der für die lockAnweisung ausgegebene
LukeH

@ArsenMkrt, sie werden nicht in der Warteschlange "Blockiert" gehalten. Ich denke, es gibt einen Unterschied zwischen Schlaf und
Blockzustand

Welchen Unterschied meinst du mit @Mohanavel?
Arsen Mkrtchyan

1
Das war nicht die Frage. Die Frage betraf das Schlüsselwort "Sperre". Angenommen, ein Prozess gibt einen Abschnitt "Sperren" ein. Das bedeutet, dass der Prozess diesen Code blockiert und kein anderer Prozess diesen Abschnitt betreten kann, bis diese Sperre aufgehoben wird. Nun ... jetzt versuchen 2 weitere Prozesse, denselben Block einzugeben. Da es durch das Schlüsselwort "lock" geschützt ist, warten sie gemäß den Angaben in diesem Forum. Wenn der erste Prozess die Sperre aufhebt. Welcher Prozess tritt in den Block ein? der erste, der versucht einzutreten oder der letzte?
jstuardo

1
Ich denke du meinst Thread statt Prozess ... wenn ja, dann lautet die Antwort Nein, es gibt keine Garantie, welche eingegeben wird ... mehr hier stackoverflow.com/questions/4228864/…
Arsen Mkrtchyan

29

Sperren verhindern, dass andere Threads den im Sperrblock enthaltenen Code ausführen. Die Threads müssen warten, bis der Thread im Sperrblock abgeschlossen ist und die Sperre aufgehoben wird. Dies wirkt sich negativ auf die Leistung in einer Multithread-Umgebung aus. Wenn Sie dies tun müssen, sollten Sie sicherstellen, dass der Code innerhalb des Sperrblocks sehr schnell verarbeitet werden kann. Sie sollten versuchen, teure Aktivitäten wie den Zugriff auf eine Datenbank usw. zu vermeiden.


11

Die Auswirkungen auf die Leistung hängen von der Art und Weise ab, wie Sie sperren. Eine gute Liste der Optimierungen finden Sie hier: http://www.thinkingparallel.com/2007/07/31/10-ways-to-reduce-lock-contention-in-threaded-programs/

Grundsätzlich sollten Sie versuchen, so wenig wie möglich zu sperren, da dadurch Ihr Wartecode in den Ruhezustand versetzt wird. Wenn Sie umfangreiche Berechnungen oder lang anhaltenden Code (z. B. das Hochladen von Dateien) in einer Sperre haben, führt dies zu einem enormen Leistungsverlust.


1
Der Versuch, Low-Lock-Code zu schreiben, kann jedoch häufig zu subtilen, schwer zu findenden und zu behebenden Fehlern führen, selbst wenn Sie ein Experte auf diesem Gebiet sind. Die Verwendung eines Schlosses ist oft das geringere von zwei Übeln. Sie sollten genau so viel sperren, wie Sie brauchen, nicht mehr und nicht weniger!
LukeH

1
@LukeH: Es gibt einige Verwendungsmuster, bei denen Low-Lock-Code sehr einfach und leicht sein kann [ do { oldValue = thing; newValue = updated(oldValue); } while (CompareExchange(ref thing, newValue, oldValue) != oldValue]. Die größte Gefahr besteht darin, dass es schwierig sein kann, den Code anzupassen, wenn sich die Anforderungen über das hinaus entwickeln, was mit solchen Techniken möglich ist.
Supercat

Link ist kaputt.
CarenRose

8

Der Teil in der lock-Anweisung kann nur von einem Thread ausgeführt werden, sodass alle anderen Threads unbegrenzt darauf warten, dass der Thread, der die Sperre hält, beendet wird. Dies kann zu einem sogenannten Deadlock führen.


8

Die lockAnweisung wird in Aufrufe der Enterund Exit-Methoden von übersetzt Monitor.

Die lockAnweisung wartet auf unbestimmte Zeit, bis das Sperrobjekt freigegeben wird.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.