std :: lock_guard Beispiel, Erklärung, warum es funktioniert

8

Ich habe einen Punkt in meinem Projekt erreicht, der die Kommunikation zwischen Threads auf Ressourcen erfordert, auf die sehr gut geschrieben werden kann, so dass Synchronisation ein Muss ist. Allerdings verstehe ich Synchronisation nicht wirklich etwas anderes als die grundlegende Ebene.

Betrachten Sie das letzte Beispiel in diesem Link: Ссылка

%Vor%

Das Beispiel zeigt, wie drei Threads, zwei Writer und ein Reader, auf eine gemeinsame Ressource (Liste) zugreifen.

Es werden zwei globale Funktionen verwendet: eine, die von den beiden Writer-Threads verwendet wird, und eine, die vom Leser-Thread verwendet wird. Beide Funktionen verwenden einen lock_guard, um dieselbe Ressource, die Liste, zu sperren.

Nun, hier ist, was ich nicht um den Kopf wickeln kann: Der Leser verwendet eine Sperre in einem anderen Bereich als die beiden Writer-Threads und sperrt trotzdem die gleiche Ressource. Wie kann das funktionieren? Mein begrenztes Verständnis von Mutexen eignet sich gut für die Writer-Funktion, da gibt es zwei Threads, die die exakt gleiche Funktion verwenden. Ich kann das verstehen, eine Überprüfung wird richtig gemacht, wenn Sie den geschützten Bereich betreten wollen, und wenn jemand anderes schon drinnen ist, warten Sie.

Aber wenn der Umfang anders ist? Dies würde anzeigen, dass es eine Art von Mechanismus gibt, der leistungsfähiger ist als der Prozess selbst, eine Art Laufzeitumgebung, die die Ausführung des "späten" Threads blockiert. Aber ich dachte, es gäbe keine solchen Dinge in C ++. So bin ich ratlos.

Was genau geht hier unter der Haube vor?

    
Deviatore 07.02.2016, 10:04
quelle

3 Antworten

4

myMutex ist global, was zum Schutz von myList verwendet wird. guard(myMutex) greift einfach in das Schloss und der Ausgang aus dem Block verursacht seine Zerstörung, wodurch das Schloss gelöst wird. guard ist nur eine bequeme Möglichkeit, das Schloss zu aktivieren und zu deaktivieren.

Damit wird mutex keine Daten schützen. Es bietet nur eine Möglichkeit zum Schutz von Daten. Es ist das Entwurfsmuster, das Daten schützt. Wenn ich also meine eigene Funktion schreibe, um die Liste wie folgt zu ändern, kann mutex sie nicht schützen.

%Vor%

Die Sperre funktioniert nur, wenn alle Code-Teile, die auf die Daten zugreifen müssen, die Sperre aktivieren, bevor sie auf sie zugreifen und sie wieder loslassen, nachdem sie ausgeführt wurden. Dieses Design-Muster des Ein- und Ausschaltens der Sperre vor und nach jedem Zugriff schützt die Daten ( myList in Ihrem Fall)

Nun würden Sie sich fragen, warum mutex überhaupt verwendet wird und warum nicht, sagen wir, bool . Und ja, Sie können, aber Sie müssen sicherstellen, dass die Variable bool bestimmte Eigenschaften aufweist, einschließlich, aber nicht beschränkt auf die folgende Liste.

  1. Wird nicht über mehrere Threads zwischengespeichert (flüchtig).
  2. Lesen und Schreiben wird eine atomare Operation sein.
  3. Ihre Sperre kann Situationen behandeln, in denen mehrere Ausführungspipelines (logische Kerne usw.) vorhanden sind.

Es gibt verschiedene synchronization -Mechanismen, die "besseres Sperren" (über Prozesse im Vergleich zu Threads, mehrere Prozessoren im Vergleich zu Einzelprozessoren usw.) zu Kosten einer "langsameren Leistung" bieten. Daher sollten Sie immer einen Sperrmechanismus wählen, der ist gerade genug für deine Situation.

    
Vikhram 07.02.2016, 11:14
quelle
9

Sehen wir uns die entsprechende Zeile an:

%Vor%

Beachten Sie, dass lock_guard auf den globalen Mutex myMutex verweist. Das heißt, der gleiche Mutex für alle drei Threads. Was lock_guard macht, ist im Wesentlichen das:

  • Bei der Konstruktion wird myMutex gesperrt und ein Verweis darauf beibehalten.
  • Bei der Zerstörung (d. h. wenn der Schutzbereich des Schutzes verlassen wird), wird myMutex freigegeben.

Der Mutex ist immer derselbe, er hat nichts mit dem Oszilloskop zu tun. Der Punkt lock_guard dient lediglich dazu, das Sperren und Entsperren des Mutex für Sie einfacher zu machen. Wenn Sie beispielsweise lock / unlock manuell setzen, aber Ihre Funktion irgendwo in der Mitte eine Ausnahme auslöst, wird sie nie die unlock -Anweisung erreichen. Wenn Sie also den manuellen Weg Sie machen, müssen Sie sicherstellen, dass der Mutex immer freigeschaltet ist. Auf der anderen Seite wird das Objekt lock_guard automatisch zerstört, wenn die Funktion beendet wird - unabhängig davon, wie sie beendet wird.

    
mindriot 07.02.2016 10:11
quelle
2

Genau das macht eine Sperre. Wenn ein Thread die Sperre übernimmt, unabhängig davon, wo im Code dies geschieht, muss er warten, bis ein anderer Thread die Sperre hält. Wenn ein Thread eine Sperre freigibt, unabhängig davon, wo im Code dies geschieht, kann ein anderer Thread diese Sperre erlangen.

Sperren schützen Daten, nicht Code. Sie tun dies, indem sie sicherstellen, dass der gesamte Code, der auf die geschützten Daten zugreift, während sie die Sperre hält, andere Threads von jedem Code ausschließt, die möglicherweise auf dieselben Daten zugreifen.

    
David Schwartz 07.02.2016 10:09
quelle