4 Stimmen

Macht die sperrfreie Multithreading-Programmierung irgendetwas einfacher?

Ich habe nur ein bisschen über dieses Thema gelesen, aber es scheint, dass der einzige Vorteil darin besteht, Konfliktprobleme zu umgehen, aber keinen wichtigen Einfluss auf das Deadlock-Problem haben wird, da der code, der frei von Sperren ist, so klein und grundlegend ist (FIFOs, LIFOs, Hash), dass es nie ein Deadlock-Problem gab.

Also geht es nur um Leistung - ist das richtig?

9voto

jerryjvl Punkte 18807

Die programmierung ohne Sperren dreht sich (soweit ich sehen kann) immer um Leistung, da die Verwendung eines Sperres in den meisten Fällen viel einfacher ist und daher bevorzugt wird.

Beachten Sie jedoch, dass Sie bei der programmierung ohne Sperren möglicherweise einen Deadlock gegen ein Live-Lock eintauschen, was viel schwieriger zu diagnostizieren ist, da meines Wissens keine Tools darauf ausgelegt sind, es zu diagnostizieren (obwohl ich mich da täuschen könnte).

Ich würde sagen, gehen Sie nur den Weg ohne Sperren, wenn Sie müssen; das heißt, Sie haben ein Szenario, in dem Sie ein stark umkämpftes Sperre haben, das Ihre Leistung beeinträchtigt. (Wenn es nicht kaputt ist, reparieren Sie es nicht).

9voto

Paar Probleme.

Bald stehen uns Desktop-Systeme mit 64, 128 und 256 Kernen gegenüber. Parallismus in diesem Bereich ist anders als unsere bisherige Erfahrung mit 2, 4, 8 Kernen; die Algorithmen, die auf solchen kleinen Systemen erfolgreich ausgeführt werden, werden auf hochparallelen Systemen aufgrund von Konflikten langsamer laufen.

In diesem Sinne ist lock-frei wichtig, da es maßgeblich zur Lösung der Skalierbarkeit beiträgt.

Es gibt auch einige sehr spezifische Bereiche, in denen lock-frei äußerst praktisch ist, wie zum Beispiel im Windows-Kernel, wo Ausführungsmodi existieren, in denen Schlafzustände jeglicher Art (wie Warten) verboten sind, was offensichtlich sehr einschränkend in Bezug auf Datenstrukturen ist, aber wo lock-frei eine gute Lösung bietet.

Außerdem haben lock-freie Datenstrukturen oft keine Fehlermodi; sie können tatsächlich nicht versagen, während lock-basierte Datenstrukturen natürlich an ihren Sperren scheitern können. Sich keine Gedanken über Versagen machen zu müssen, vereinfacht den Code.

Ich habe eine Bibliothek von lock-freien Datenstrukturen geschrieben, die ich bald veröffentlichen werde. Ich denke, wenn ein Entwickler Zugriff auf eine gut erprobte API erhält, kann er sie einfach verwenden - es spielt keine Rolle, ob sie lock-frei ist oder nicht, er muss sich nicht um die Komplexität in der zugrunde liegenden Implementierung kümmern - und das ist der richtige Weg.

1voto

Timo Geusch Punkte 23597

Es geht auch um Skalierbarkeit. Um heutzutage Leistungssteigerungen zu erzielen, müssen Sie die Probleme, an denen Sie arbeiten, parallelisieren, damit Sie sie über mehrere Kerne skalieren können - je mehr, desto besser.

Der traditionelle Weg, dies zu tun, besteht darin, Datenstrukturen zu sperren, die parallelen Zugriff erfordern, aber je mehr Threads Sie wirklich parallel ausführen können, desto größer wird dieser Flaschenhals.

Also ja, es geht um Leistung...

1voto

Für präemptives Threading können Threads, die beim Halten eines Sperrschlosses angehalten werden, Threads blockieren, die sonst Fortschritte machen würden. Sperrschlossfrei hat dieses Problem nicht, da laut Herlihys Definition ein anderer Thread immer Fortschritte machen kann.

Für nicht-präemptives Threading spielt das nicht so eine große Rolle, da selbst sperrschlossbasierte Lösungen nach Herlihys Definition sperrschlossfrei sind.

1voto

pierre Punkte 11

Dies handelt von Leistungen - aber auch von der Fähigkeit, Mehrfadenlasten zu bewältigen:

  • Sperren gewähren einen exklusiven Zugriff auf einen Teil des Codes: Während ein Faden eine Sperre hat, werden andere Fäden gedreht (Schleife, während sie versuchen, die Sperre zu erwerben) oder blockiert, schlafen, bis die Sperre freigegeben wird (was normalerweise passiert, wenn das Drehen zu lange dauert);

  • Atomare Operationen gewähren einen exklusiven Zugriff auf eine Ressource (normalerweise eine wortgroße Variable oder einen Zeiger), indem sie nicht unterbrechbare intrinsische CPU-Anweisungen verwenden.

Da Sperren die Ausführung anderer Fäden BLOCKIEREN, wird ein Programm verlangsamt. Da atomare Operationen seriell ausgeführt werden (nacheinander), gibt es kein Blockieren*.

(*) solange die Anzahl der gleichzeitigen CPUs, die versuchen, auf die gleiche Ressource zuzugreifen, keine Engpässe verursacht - aber bisher haben wir nicht genügend CPU-Kerne, um dies als Problem zu betrachten.

Ich habe daran gearbeitet, einen Wartefreien (Sperrfreien ohne Wartezeiten) Schlüssel-Wert-Speicher für den Server zu schreiben, an dem ich arbeite.

Bibliotheken wie Tokyo Cabinet (auch TC-FIXED, ein einfaches Array) verlassen sich auf Sperren, um die Integrität einer Datenbank zu bewahren:

"Während ein schreibender Faden die Datenbank bedient, sind andere lesende Fäden und schreibende Fäden blockiert" (Tokyo Cabinet Dokumentation)

Die Ergebnisse eines Tests ohne Nebenläufigkeit (ein Ein-Faden-Test):

SQLite   Zeit: 56,4 ms (ein B-Baum)
TC       Zeit: 10,7 ms (eine Hashtabelle)
TC-FIXED Zeit:  1,3 ms (ein Array)
G-WAN KV Zeit:  0,4 ms (etwas Neues, das funktioniert, aber ich bin mir nicht sicher, ob ein Name benötigt wird)

Mit Nebenläufigkeit (mehrere Fäden schreiben und lesen in derselben DB) überlebte nur der G-WAN KV denselben Test, weil (im Gegensatz zu den anderen) er niemals blockiert.

Also, ja, dieser KV-Speicher macht es Entwicklern leichter, ihn zu verwenden, da sie sich nicht um Fadenprobleme kümmern müssen. Es war jedoch nicht trivial, es so zum Laufen zu bringen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X