Wie verwende ich eine beliebige Zeichenfolge als Sperre in C ++?

8

Nehmen wir an, ich habe ein Multithread-C ++ - Programm, das Anfragen in Form eines Funktionsaufrufs an handleRequest(string key) bearbeitet. Jeder Aufruf von handleRequest erfolgt in einem separaten Thread und es gibt eine beliebig große Anzahl möglicher Werte für key .

Ich möchte folgendes Verhalten:

  • Simultane Aufrufe von handleRequest(key) werden serialisiert, wenn sie für key den gleichen Wert haben.
  • Die globale Serialisierung ist minimiert.

Der Körper von handleRequest könnte so aussehen:

%Vor%

Frage: Wie würde ich KeyLock implementieren, um das erforderliche Verhalten zu erhalten?

Eine naive Implementierung könnte so beginnen:

%Vor%

... aber das erfordert eine globale Sperre am Anfang und Ende jeder Anfrage und die Erstellung eines separaten Lock -Objekts für jede Anfrage. Wenn der Konflikt zwischen den Aufrufen von handleRequest hoch ist, ist das möglicherweise kein Problem, aber es könnte viel Overhead verursachen, wenn die Konkurrenz gering ist.

    
eschercycle 03.10.2008, 18:34
quelle

6 Antworten

12

Sie könnten etwas ähnliches wie in Ihrer Frage tun, aber statt einer einzigen global_key_map haben Sie mehrere (wahrscheinlich in einem Array oder Vektor) - welche verwendet wird, wird durch eine einfache Hash-Funktion in der Zeichenfolge bestimmt.

Auf diese Weise verteilen Sie das anstelle einer einzelnen globalen Sperre auf mehrere unabhängige.

Dies ist ein Muster, das oft in Speicherzuordnern verwendet wird (ich weiß nicht, ob das Muster einen Namen hat - es sollte). Wenn eine Anfrage eingeht, bestimmt etwas, aus welchem ​​Pool die Zuweisung stammt (normalerweise die Größe der Anfrage, aber auch andere Parameter können berücksichtigt werden), dann muss nur dieser Pool gesperrt werden. Wenn eine Zuordnungsanforderung von einem anderen Thread eingeht, der einen anderen Pool verwendet, gibt es keine Sperrkonkurrenz.

    
Michael Burr 03.10.2008, 18:38
quelle
2

Es hängt von der Plattform ab, aber die zwei Techniken, die ich ausprobieren würde, wären:

  • Benutzt Mutex / Synchronisation Objekte, wobei Objektname = Schlüssel
  • Verwenden Sie das Dateisystem-basierte Sperren, wo Sie Versuchen Sie, eine Nicht-Freigabe zu erstellen temporäre Datei mit dem Schlüsselnamen. Wenn es bereits existiert (= schon gesperrt) das wird scheitern und du wirst muss abfragen, um es erneut zu versuchen

Beide Techniken hängen von den Details Ihres Betriebssystems ab. Experimentieren Sie und sehen Sie, was funktioniert. .

    
Roddy 03.10.2008 18:39
quelle
2

Vielleicht wäre ein std::map<std::string, MutexType> , was Sie wollen, wobei MutexType der Typ des gewünschten Mutex ist. Wahrscheinlich müssen Sie die Zugriffe auf die Map in einem anderen Mutex umbrechen, um sicherzustellen, dass kein anderer Thread zur gleichen Zeit eingefügt wird (und denken Sie daran, die Überprüfung erneut durchzuführen, nachdem der Mutex gesperrt wurde, um sicherzustellen, dass ein anderer Thread den Thread nicht hinzugefügt hat während Sie auf den Mutex warten!).

Das gleiche Prinzip könnte für jede andere Synchronisierungsmethode gelten, beispielsweise für einen kritischen Abschnitt.

    
coppro 03.10.2008 18:39
quelle
2

Erhöhen Sie die Granularität und sperren Sie ganze Schlüsselbereiche

Dies ist eine Variante der Antwort von Mike B, bei der Sie anstelle mehrerer flüssiger Sperrkarten eine einzige feste Reihe von Sperren haben, die für Schlüsselbereiche statt für einzelne Schlüssel gelten.

Vereinfachtes Beispiel: Erstellen Sie beim Start ein Array mit 256 Sperren, und verwenden Sie dann das erste Byte des Schlüssels, um den Index der zu erfassenden Sperre zu bestimmen (d. h. alle mit 'k' beginnenden Schlüssel werden von locks[107] geschützt).

Um den optimalen Durchsatz aufrechtzuerhalten, sollten Sie die Verteilung der Schlüssel und die Konkurrenzrate analysieren. Die Vorteile dieses Ansatzes sind null dynamische Zuordnungen und einfache Bereinigung; Sie vermeiden auch eine zweistufige Verriegelung. Der Nachteil sind mögliche Konkurrenzspitzen, wenn die Schlüsselverteilung im Zeitverlauf verzerrt wird.

    
Constantin 03.10.2008 22:18
quelle
0

Nachdem Sie darüber nachgedacht haben, könnte ein anderer Ansatz etwa so aussehen:

  • In handleRequest , erstellen Sie eine Callback , die die eigentliche Arbeit erledigt.
  • Erstellen Sie ein multimap<string, Callback*> global_key_map , geschützt durch einen Mutex.
  • Wenn ein Thread sieht, dass key bereits verarbeitet wird, fügt er Callback* zu global_key_map hinzu und gibt
  • zurück
  • Andernfalls ruft es seinen Callback sofort auf und ruft dann die Callbacks auf, die in der Zwischenzeit für denselben Schlüssel angezeigt wurden.

Folgendes wurde implementiert:

%Vor%

Das hat den Vorteil, Threads freizugeben, die sonst auf eine Tastensperre warten würden, aber abgesehen davon ist es ziemlich genau so wie die naive Lösung, die ich in der Frage gepostet habe.

Es könnte jedoch mit den Antworten von Mike B und Constantin kombiniert werden.

    
eschercycle 03.10.2008 22:47
quelle
0
%Vor%     
ramneek handa 05.01.2009 03:57
quelle