C ++ 11 reentrante Klassensperrstrategie

8

Ich habe eine Schnittstelle mit dem pimpl idiom, aber die Schnittstelle muss reentrant sein. Das Aufrufen von Threads muss jedoch nicht die Sperre berücksichtigen. Dies ist eine vierteilige Frage und ein Teil unentgeltlich konstruiertes C ++ 11-Beispiel (Beispiel enthalten, um mehrere FAQ-ähnliche Fragen zu beantworten, die ich über re: locking , pimpl , rvalue und C ++ 11, wo die Antworten waren in ihrer Qualität etwas zweifelhaft).

In der Kopfzeile example.hpp:

%Vor%

Und dann in der Implementierung:

%Vor%

Und meine Fragen sind:

  1. Gibt es eine bessere Möglichkeit, das Sperren innerhalb der privaten Implementierung zu behandeln?
  2. Besteht tatsächlich ein Schaden, wenn Impl öffentlich gemacht wird, da die Definition undurchsichtig ist?
  3. Bei der Verwendung von clang's -O4 zur Aktivierung der Link-Zeit-Optimierung sollte es für den Linker möglich sein, den Link zu umgehen Dereferenzierungsaufwand von std::unique_ptr . Hat das jemand verifiziert?
  4. Gibt es eine Möglichkeit, foo_set("asdf") aufzurufen und das Beispiel korrekt zu verknüpfen? Wir haben Probleme herauszufinden, was die korrekte explizite Template-Instanziierung für const char[6] ist. Für jetzt habe ich eine Template-Spezialisierung eingerichtet, die ein std::string -Objekt erstellt und einen Aufruf an foo_set () anfordert. Alles in allem scheint dies der beste Weg nach vorne zu sein, aber ich würde gerne wissen, wie man "asdf" und std::decay das Ergebnis weitergibt.

In Bezug auf die Sperrstrategie habe ich aus verschiedenen Gründen eine offensichtliche Tendenz dazu entwickelt:

  • Ich kann den Mutex gegebenenfalls als exklusiven Mutex ausschließen
  • Durch das Entwerfen der Impl-API, die die erforderliche Sperre enthält, gibt es eine sehr starke Kompilierzeitgarantie für die Sperrsemantik
  • Es ist schwer zu vergessen, etwas zu sperren (und einen "einfachen API" -Bug, wenn das passiert, der Compiler fängt das wieder auf, sobald die API repariert wurde)
  • Es ist schwierig, etwas gesperrt zu lassen oder aufgrund von RAII eine Dead-Sperre zu erzeugen und der Impl keinen Verweis auf den Mutex zu geben
  • Durch die Verwendung von Vorlagen entfällt das Downgrade von einer eindeutigen Sperre auf eine gemeinsame Sperre
  • Da diese Sperrstrategie mehr Code umfasst, als tatsächlich benötigt wird, erfordert es explizite Anstrengungen, eine Sperre von "eindeutig" auf "gemeinsam" herabzustufen, wodurch das allzu häufige Szenario behandelt wird, in dem bei der gemeinsamen Sperrung vorgenommene Annahmen erneut getestet werden müssen ein eindeutiger gesperrter Bereich
  • Fehlerkorrekturen oder Impl-API-Änderungen erfordern keine Neukompilierung der gesamten Anwendung, da die API von example.hpp extern korrigiert wurde.

Ich habe gelesen, dass ACE auch diese Art der Sperrstrategie verwendet, aber ich ' Ich begrüße einige Kritik aus der realen Welt von ACE-Benutzern oder anderen, die das Schloss als notwendiger Teil der Schnittstelle weitergeben.

Der Vollständigkeit halber, hier ist eine example_main.cpp für Leute, um zu kauen.

%Vor%

Und Anweisungen erstellen, um C++11 und libc++ zu verwenden:

%Vor%

Als kleinen Bonus habe ich dieses Beispiel aktualisiert, um die perfekte Weiterleitung mit Hilfe von rvalue-Referenzen in der foo_set() -Methode einzuschließen. Obwohl es nicht perfekt war, dauerte es länger als ich erwartet hatte, die Template Instanziierung richtig zu bekommen, was ein Problem beim Linken ist. Dazu gehören auch die entsprechenden Überladungen für C-Basistypen, einschließlich: char* , const char* , char[N] und const char[N] .

    
Sean 05.11.2012, 20:40
quelle

2 Antworten

1

Für Frage 1 wäre eine Sache, die ich versucht wäre, SFINAE zu verwenden, um die Lock-Typen einzuschränken, die als LockType erlaubt in shared_lock_t oder unique_lock_t übergeben wurden.

Ie:

%Vor%

... aber das wird ein bisschen ausführlich.

Dies bedeutet, dass das Übergeben eines falschen Lock-Typs den Fehler "Nichts entspricht" ergibt. Ein anderer Ansatz wäre, zwei verschiedene bar_capacity zu haben, die shared_lock_t und unique_lock_t exponiert haben, und eine private bar_capacity, die eine Vorlage LockType akzeptiert.

Wie geschrieben, ist jeder Typ mit einer .owns_lock() -Methode, die einen in bool konvertierbaren Typ zurückgibt, ein gültiges Argument ...

    
Yakk 11.11.2012 18:59
quelle
1

Unter Verwendung eines Pimpl-Idioms sollte der Mutex Teil der Implementierung sein. Dies ermöglicht es Ihnen, zu meistern, wenn die Sperre gestartet wird.

Übrigens, warum ein unique_lock verwenden, wenn ein lock_guard ausreicht?

Ich sehe keinen Vorteil darin, die Öffentlichkeit bekannt zu machen.

std :: unique_ptr sollte für die meisten modernen Compiler genauso effizient sein wie ein Zeiger. Nicht verifiziert jedoch.

Ich würde das const char [N] foo_set nicht als

weiterleiten %Vor%

aber wie

%Vor%

Dies vermeidet die String-Erstellung in der Header-Datei und lässt die Implementierung alles Notwendige tun.

    
Vicente Botet Escriba 15.12.2012 17:19
quelle