web-dev-qa-db-de.com

boost :: unique_lock vs boost :: lock_guard

Ich verstehe den Unterschied zwischen diesen beiden Sperrklassen nicht gut. In der Boost-Dokumentation heißt es, dass boost::unique_lock Die Sperre nicht automatisch erkennt.

Bedeutet dies, dass der Hauptunterschied zwischen unique_lock Und lock_guard Darin besteht, dass wir bei unique_lock Die Funktion lock() explizit aufrufen müssen?

58
Guillaume07

Beantworten Sie zuerst Ihre Frage. Nein, Sie müssen nicht lock für ein unique_lock aufrufen. Siehe unten:

Das unique_lock ist nur eine Sperrklasse mit mehr Funktionen. In den meisten Fällen macht der lock_guard das, was Sie wollen und es wird ausreichen.
Das unique_lock bietet Ihnen weitere Funktionen. Zum Beispiel eine zeitgesteuerte Wartezeit, wenn Sie eine Zeitüberschreitung benötigen oder Ihre Sperre auf einen späteren Zeitpunkt als die Konstruktion des Objekts verschieben möchten. Es hängt also stark davon ab, was Sie tun möchten. Übrigens: Die folgenden Code-Schnipsel machen dasselbe.

boost::mutex mutex;
boost::lock_guard<boost::mutex> lock(mutex);
boost::mutex mutex;
boost::unique_lock<boost::mutex> lock(mutex);

Der erste kann verwendet werden, um den Zugriff auf Daten zu synchronisieren. Wenn Sie jedoch Bedingungsvariablen verwenden möchten, müssen Sie den zweiten auswählen.

60
mkaes

Die derzeit am besten gewählte Antwort ist gut, hat aber meinen Zweifel nicht geklärt, bis ich etwas tiefer gegraben habe und mich entschlossen habe, sie mit Leuten zu teilen, die vielleicht im selben Boot sitzen.

Zunächst folgen sowohl lock_guard Als auch unique_lock Dem RAII-Muster, im einfachsten Anwendungsfall wird das Schloss während des Aufbaus erfasst und während der Zerstörung automatisch entriegelt. Wenn dies Ihr Anwendungsfall ist, benötigen Sie nicht die zusätzliche Flexibilität von unique_lock, Und lock_guard Ist effizienter.

Der Hauptunterschied zwischen beiden ist, dass eine unique_lock - Instanz nicht immer den Mutex besitzen muss, dem sie zugeordnet ist, während sie in lock_guard Den Mutex besitzt. Dies bedeutet, dass unique_lock Ein zusätzliches Flag haben muss, das angibt, ob es die Sperre besitzt, und eine weitere zusätzliche Methode 'owns_lock ()', um dies zu überprüfen. Wenn wir das wissen, können wir alle zusätzlichen Vorteile erklären, die dieses Flag mit dem Overhead dieser zusätzlichen Daten bringt, die gesetzt und überprüft werden müssen

  1. Das Schloss muss nicht direkt am Bau genommen werden, Sie können das Flag std::defer_lock Während des Baus passieren, um den Mutex während des Baus nicht zu blockieren.
  2. Wir können es entsperren, bevor die Funktion endet, und müssen nicht unbedingt warten, bis der Destruktor es freigibt, was praktisch sein kann.
  3. Sie können das Eigentum an der Sperre von einer Funktion übergeben, es ist beweglich und nicht kopierbar.
  4. Es kann mit bedingten Variablen verwendet werden, da hierfür Mutex gesperrt, Bedingung geprüft und entsperrt werden muss, während auf eine Bedingung gewartet wird.
44
jayadev

Ihre Implementierung finden Sie unter path .../boost/thread/locks.hpp - und sie sitzen nebeneinander :) Um es kurz zu machen:

lock_guard ist eine kurze einfache Utility-Klasse, die Mutex im Konstruktor sperrt und Destruktor entsperrt, ohne sich um Details zu kümmern.

nique_lock ist etwas komplexer und fügt ziemlich viele Funktionen hinzu - aber es wird trotzdem automatisch im Konstruktor gesperrt. Es heißt unique_lock, weil es das Konzept des "Lock Ownership" einführt (siehe owns_lock () -Methode).

14

Wenn Sie an pthreads(3) gewöhnt sind:

  • boost::mutex = pthread_mutex_*
  • boost::unique_lock = pthread_rwlock_* Zum Abrufen von Schreib-/Exklusivsperren (d. H. pthread_rwlock_wrlock)
  • boost::shared_lock = pthread_rwlock_* Zum Abrufen von gelesenen/freigegebenen Sperren (d. H. pthread_rwlock_rdlock)

Ja, ein boost::unique_lock Und ein boost::mutex Funktionieren auf ähnliche Weise, aber ein boost::mutex Ist im Allgemeinen ein Mutex mit geringerem Gewicht, der erworben und freigegeben werden kann. Das heißt, ein shared_lock Mit der bereits erworbenen Sperre ist schneller (und ermöglicht Parallelität), aber es ist vergleichsweise teuer, einen unique_lock Zu erhalten.

Sie müssen unter die Decke schauen, um die Implementierungsdetails zu sehen, aber das ist der Kern der beabsichtigten Unterschiede.


Apropos Leistung: Hier ist ein mäßig nützlicher Vergleich der Latenzen:

http://www.eecs.berkeley.edu/%7Ercs/research/interactive_latency.html

Es wäre schön, wenn ich/jemand die relativen Kosten der verschiedenen pthread_ * -Primitive vergleichen könnte, aber zuletzt habe ich nachgeschaut, dass pthread_mutex_* ~ 25us betrug, während pthread_rwlock_* ~ 20-100us betrug, abhängig davon, ob oder nicht, die Lesesperre wurde bereits erworben (~ 10us) oder nicht (~ 20us) oder der Schreiber (~ 100us). Sie müssen einen Benchmark durchführen, um die aktuellen Zahlen zu bestätigen, und ich bin mir sicher, dass dies sehr betriebssystemspezifisch ist.

6
Sean

Ich denke, unique_lock kann auch verwendet werden, wenn Sie den Unterschied zwischen eindeutigen und gemeinsam genutzten Sperren hervorheben müssen.

1
Alexey Petrenko