2017-12-24 25 views
10

Ich fragte mich, wo es am besten wäre, über so etwas zu fragen, also stellte ich diese Frage auf Meta (https://meta.stackexchange.com/questions/304981) und wurde hier gerichtet, also hier geht.Wie absl :: Mutex bedingte kritische Abschnitte Leseaufwecker behandeln

Ich war wirklich neugierig, welche Optimierungen in Googles Implementierung von bedingten kritischen Abschnitten mit absl::Mutex (https://github.com/abseil/abseil-cpp/blob/master/absl/synchronization/mutex.h) eingebaut wurden. Insbesondere habe ich mich gefragt, wie sie mit Leseaufweckern umgehen, wenn der Zustand eines Lesers wahr wird. Wecken sie auch alle anderen Leser auf der Warteliste auf? This line scheint zu signalisieren, dass sie tun .. Ist das nicht riskieren ein O (n) Traversal jedes Mal und auch donnernden Herden in einem Schreibpriorität Mutex riskieren?

+0

Wenn man nur die Include-Dateien in mutex.cc liest, sieht man, dass sie pthread oder window.h benutzen. Mit dieser High-Level-Threading-Bibliothek können Sie wahrscheinlich nichts Magisches tun. Was auch immer das ist das System, das Threads aufweckt. Mit dem niedrigeren Code können Sie steuern, welcher Leser wach werden soll (oder wählen Sie mindestens einige davon aus, siehe 'man 2 futex'). Verschwenden Sie keine Zeit damit, diesen Wrapper rund um Wrapper mit Super-Layern zu lesen, die existieren, um ein Problem zu lösen, das nicht vorhanden wäre, wenn es nicht den Sublayer gäbe! – Oliv

+0

'futex' (auf Linux) ist das, was das Universum des Möglichen einschränken wird. Dann wird alles, was darum herum aufgebaut wird (Mutex, Semaphore, etc ...), ebenso oder mehr begrenzt sein. – Oliv

Antwort

4

Ich denke, es ist wichtig, die Unterschiede zwischen Design und Implementierungsoptimierung aufzuzeigen. Es scheint, dass absl :: dem Design einen höheren Stellenwert einräumt, indem es eine Implementierung bereitstellt, die diese Designs unterstützt, im Gegensatz zu besser optimierten Komponenten. Manchmal ist das bessere Design inhärent leistungsfähiger als das schwerfälligere, so dass das Ergebnis auf beide Faktoren optimiert ist (Synergie). Absiel.io diskutiert das genauer.

Aus dem Blick auf ihre Quelle, Absl scheint nicht zu versuchen Magie zu vermeiden, an die ausstehenden Leser zu senden, also ja, die donnernden Herde bleibt ein Problem. Im Allgemeinen kann keine Implementierung dieses Problem sinnvoll lösen. Es ist ein Designproblem.

Durch das Einbetten der Bedingung als Teil der Sperre ermöglicht absl: dem Programmierer, dieses Problem zu vermeiden oder zu entwerfen. Es gibt immer noch eine Herde, aber die Schlossimplementierung ist in der Lage, sie zu verdünnen plan 9 nod here. Wenn Sie Ihre Gründe für ein gutes Erwachen entwerfen, zum Beispiel Bits in einer Maske, können Sie gemeinsam genutzte/exklusive Sperren sicher verwenden, ohne sich über unerwünschte Wake-ups Gedanken machen zu müssen, die sich auf Ihre Leistung auswirken.

Dies ist die Essenz einer "Entwurfsoptimierung": Das Problem wird in der Quelle genauer beschrieben und die resultierende Implementierung ist von höherer Qualität (Leistung, Skalierung, ...) als sonst.

Plan9. Bedingungsvariablen entsprechen dem Unix-Kernel-Konzept des Schlafs (nicht der ersten) und des Aufweckens. Wenn Sie feststellen, dass sich ein Objekt nicht in dem von Ihnen benötigten Zustand befindet, führen Sie eine Aktion aus, um es in diesen Zustand zu versetzen, und warten Sie, bis dieser Zustand erreicht ist. Zum Beispiel könnten Sie ein Objekt in einem Cache suchen, und wenn es nicht vorhanden ist, erstellen Sie ein ungültiges Objekt und fordern Sie an, dass es gültig gemacht wird. Dies ermöglicht eine Art Symmetrie zwischen allen Cacheteilnehmern: Sie sperren das Objekt, evaluieren oder aktualisieren seinen Zustand, entriegeln dann gleichzeitig das Objekt und warten auf weitere Änderungen an ihm.

Das hässliche Problem ist, dass es ein: nicht so etwas wie gleichzeitig gibt, und b: das Schloss Handling ist fehleranfällig (halte ich andere Sperren? Kann dies zu Deadlock führen?) Und nicht gut im Programm ausgedrückt . Plan9 Schlaf und Wakeup elegant verschmolzen diese, so dass an dem Punkt, wo die Bedingung überprüft wird, ist es für niemanden möglich, das Objekt zu beeinflussen. In der Tat ist der Punkt des referierten Artikels, wie schwer es ist, diesen einfachen Vertrag durchzusetzen. Komplizierte Verträge in einer multiprocessing Umgebung werden am besten vermieden for example.

Die kritische Optimierung (in absl :) ist, dass es nicht notwendig ist, die teure Kontextwechseloperation zu durchlaufen, um festzustellen, dass das Objekt im falschen Zustand ist, und dann wieder in den Ruhezustand zu gehen. Wenn der Code, der Sie aufwecken möchte, überprüfen kann, ob der Objektstatus für Sie interessant ist, umso besser. Ein Funktionsaufruf/Methodenaufruf ist 100 mal schneller als ein Kontextwechsel.

AbslThis section adressiert sowohl, wie die Kandidaten für Wakeup ausgewählt werden, und wie die Herde gekeult wird. This section erzeugt die Sendung.

Also der ehemalige Abschnitt wählt Elemente aus der Liste Kellner, und der letzte Abschnitt weckt sie auf. Wenn Sie sich die else-Bedingung von Zeile 2203 ansehen, wird angezeigt, wie ein Writer die Liste beendet - w_walk-> wake ist nicht wahr und wr_wait wurde gesetzt. Darüber hinaus verhindern die früheren Tests von 2015 und 2023, dass diese Liste erstellt wird, wenn es sich um ein Write-Wakeup handelt.

Monitore

obj.enter(); 
while (obj.state != StateIWant) { 
    obj.wait(); 
} 
... 
obj.exit(); 

Benötigt dieses Themas die Wache Zustand auszuführen. Die Aktion zum Verschieben dieses blockierten Threads kann 1000-mal so hoch sein wie die Kosten für die Ausführung der Bedingung. Im Gegensatz:

obj.wait(^{return obj.state == StateIWant;}); 
... 
obj.exit(); 

würde die Wartezeit Anruf erlauben, den Zustand im Zusammenhang zu bewerten von welcher auch immer Thread die Wartezeit wurde ermöglicht (dh Aufruf Ausfahrt.), So dass der teure Kontextwechsel in den nutzlosen Fällen vermieden werden.

+0

Irgendeine Idee, wofür die "designed waker" -Funktionalität in ihrem Code ist? https://github.com/abseil/abseil-cpp/blob/be40fdf1a86f4956d2f8125a7b6bd6d34e133c2d/absl/synchronization/mutex.cc#L593 Ich denke es hat etwas damit zu tun, wie die Threads aufgeweckt werden – Curious

+0

Hast du schon mal eine Tür für jemanden gehalten und hast du die Tür für eine Reihe von Leuten gehalten? Wenn ich es richtig lese, ist es ein bisschen so. Die Tür ist in diesem Fall der zugrundeliegende Synchronisationsdienst (z. B. Futex, Win32, SEM, ...), und der Code versucht, dies für eine bessere Leistung zu vermeiden. Das heißt, ich kann mir die Fälle nicht vorstellen, in denen das passiert, aber manchmal halte ich mich auch an einer Tür für Leute. – mevets

Verwandte Themen