Was ist lock-free-Multithread-Programmierung?
Ich habe gesehen, Menschen/Artikel/SO-Beiträge, die sagen, Sie haben sich entwickelt, Ihre eigene "lock-free" container für Multithread-Nutzung. Vorausgesetzt, Sie haben nicht verwendet ein performance-schlagen modulus trick (D. H. jeder thread kann nur einfügen, basierend auf modulo) wie kann man Daten-Strukturen multi-threaded, aber auch lock-frei???
Diese Frage soll in Richtung C und C++.
- lock-in der Regel kostenlos ist, bedeutet nicht "keine Sperre", es bedeutet so etwas wie transactional memory oder optimistisch-design, wo Sie nicht sperren, die für jeden Betrieb, aber einmal in eine Weile (für Rollback oder zu tun, Transaktion) - irgendeine Art von sperren benötigt werden.
- Lock-freien in der Regel bedeutet, dass Sie verwenden, compare-and-swap-hardware-Operationen.
- In einer etwas breiteren definition, die Sperre bezieht sich nicht auf die mutex-aber um das Potenzial von "einsperren" andere threads. Das offensichtlichste Beispiel ist nicht die Freigabe eines mutex, jedoch ist dies ein Beispiel für nicht-lock-freien code ohne die Verwendung eines mutex: while (X == 0) { X = 1 - X; } das Recht Gegeben scheduling von zwei threads können bleiben in dieser Schleife für immer. preshing.com/20120612/an-introduction-to-lock-free-programming
Du musst angemeldet sein, um einen Kommentar abzugeben.
Den Schlüssel in lock-free Programmierung ist die Nutzung von hardware-intrinsic atomic Operationen.
Als eine Angelegenheit von der Tat, auch Schleusen selbst verwenden müssen, diese Atomare Operationen!
Aber der Unterschied zwischen gesperrt und die lock-free Programmierung ist, dass ein lock-free-Programm kann nie angehalten werden, vollständig von jedem einzelnen thread. Im Gegensatz dazu, wenn in ein Sperr-Programm-ein thread eine Sperre und dann vom Dienst suspendiert, auf unbestimmte Zeit, das gesamte Programm blockiert und können keine Fortschritte machen. Dagegen ein lock-freies Programm Fortschritte machen kann, auch wenn die einzelnen Fäden sind auf unbestimmte Zeit ausgesetzt.
Hier ein einfaches Beispiel: Eine gleichzeitige Zähler Inkrementieren. Wir präsentieren zwei Versionen, die beide sind "thread-safe" sind, d.h. welche mehrmals aufgerufen werden kann gleichzeitig. Erste gesperrte version:
Nun die lock-freie version:
Nun stellen Sie sich vor Hunderten von Threads, rufen Sie die
increment_*
Funktion die gleichzeitig. In der gesperrten version, kein Gewinde Fortschritte machen kann, bis der lock-holding thread entsperrt den mutex. Im Gegensatz dazu, in die lock-freie version, alle threads Fortschritte machen kann. Wenn ein thread gehalten wird, wird es wohl nicht seinen Anteil der Arbeit, aber alle anderen bekommt, um Ihre Arbeit zu machen.Es ist erwähnenswert, dass im Allgemeinen lock-freie Programmierung trades Durchsatz und die mittlere Latenz Durchsatz für vorhersagbare Latenzzeit. Das heißt, dass ein lock-free-Programm wird in der Regel weniger getan, als einen entsprechenden schließ-Programm, wenn es nicht zu viel Streit (seit Atomare Operationen sind langsam und wirken viel von dem rest des Systems), aber es garantiert nie zu unvorhersehbar hohen Latenzzeiten.
std::atomic<int>
umgesetzt werden muss mit einer Sperre, wenn die hardware nicht liefern atomic-Aktualisierung einesint
. Ist es immer noch als "lock-frei", obwohl es darum geht, einen lock-in diesem Fall?is_lock_free
member-Funktionen und Makros, dies zu testen. Die einzigen Operationen, die erforderlich sind, um lock-frei, sind die instd::atomic_flag
.Für sperren, die Idee ist, dass Sie eine Sperre erwerben und dann machen Sie Ihre Arbeit wohl wissend, dass Sie niemand anderes stören können, dann die Sperre.
Für "lock-free", die Idee ist, dass Sie Ihre Arbeit machen irgendwo anders und dann versuchen, atomar verpflichten, diese Arbeit zu "visible-Status", und wiederholen, wenn Sie scheitern.
Die Probleme mit dem "lock-frei" sind, dass:
Die Kombination dieser Dinge bedeuten, dass es nur gut für relativ einfache Dinge, die unter niedrigen Streit.
Forscher entwickelt haben, Dinge wie lock-free-linked LISTS (Listen und FIFO - /FILO-Warteschlangen) und einige lock-freie Bäume. Ich glaube nicht, dass es etwas komplexer ist als diese. Wie diese Dinge funktionieren, weil es schwer ist, es ist kompliziert. Die meisten gesunden Ansatz wäre, um zu bestimmen, welche Art von Datenstruktur, die dich interessiert, dann Suche im Internet nach relevanten Forschung in lock-free-algorithmen sind für diese Datenstruktur.
Beachten Sie auch, dass es etwas namens "block frei", die wie lock-frei, außer, dass Sie wissen, Sie können immer verpflichten, die Arbeit und müssen sich nie wiederholen. Es ist sogar noch schwieriger zu design-block-free " - Algorithmus, aber Streit nicht so egal die anderen 2 Probleme mit lock-frei verschwinden. Hinweis: "gleichzeitige counter" Beispiel in Kerrek SB ' s Antwort ist nicht lock frei, sondern ist tatsächlich block frei.
increment_lockfree
ist ein lock-free-Funktion. Was auch immer Schlösser blockiert andere threads zugreifen.++counter
kompiliert wird in einer einzigenlock inc dword [counter]
Einweisung in die Montage, wo die CPU nutzt seinen cache-Kohärenz-Protokoll, um sicherzustellen, es geschieht atomar, und wo nichts (auch nicht die alten "bus-lock" - Verhalten aus dem original 8086) blockiert andere threads, oder CPUs überhaupt.Die Idee des "lock-frei" ist nicht wirklich nicht mit einer Sperre, die Idee ist die Minimierung der Anzahl von sperren und/oder kritische Abschnitte, indem Sie einige Techniken, die uns erlauben, nicht zu benutzen, sperren für die meisten Operationen.
Es erreicht werden kann, mit optimistisch-design oder transactional memory, wo Sie nicht sperren die Daten für alle Operationen, sondern nur auf einige bestimmte Punkte (wenn die Transaktion in der Transaktions-Speicher, oder wenn Sie brauchen, um roll-back in optimistisch-design).
Andere alternativen basieren auf atomaren Implementierungen einige Befehle, wie CAS (Compare Und Swap), die auch ermöglicht es uns zu lösen, die Konsens-problem gegeben eine Implementierung davon. By doing swap auf Referenzen (und kein thread arbeiten auf die gemeinsamen Daten), die CAS-Mechanismus ermöglicht uns die einfache Implementierung von lock-freien optimistisch-design (austauschen, um die neuen Daten, wenn und nur wenn niemand sich verändert haben es bereits, und dies erfolgt atomar).
Jedoch, zur Umsetzung der zugrunde liegende Mechanismus, um einer von Ihnen - einige sperren wird wahrscheinlich verwendet werden, aber die Zeit, die die Daten gesperrt ist (soll) gehalten werden, zu mindestens, wenn diese Techniken eingesetzt werden, richtig.
Die neue C-und C++ - standards (C11 und C++11) Gewinde haben und zwischen threads gemeinsam genutzt werden Atomare Datentypen und Operationen. Eine Atomare operation gibt garantiert für Operationen, die in einem Rennen zwischen zwei threads. Sobald ein thread gibt von einer solchen operation, kann es sicher sein, dass die Bedienung erfolgt in seiner Gesamtheit.
Typische Prozessor-Unterstützung für solche atomaren Operationen besteht, auf modernen Prozessoren für compare und swap (CAS) oder atomaren Schritten.
Zusätzlich zu seiend Atom -, Daten-Typ, kann das "lock-free" - Eigenschaft. Dies sollte vielleicht worden prägte "staatenlos", da diese Eigenschaft impliziert, dass ein Betrieb auf solche Art nie verlassen wird das Objekt in einem Zwischenstadium, auch wenn Sie unterbrochen wird durch einen interrupt-handler oder ein Lesen von einem anderen thread fällt in die Mitte ein update.
Mehrere Atomare Datentypen können (oder nicht) lock-free, es gibt Makros zu testen, für diese Eigenschaft. Es wird immer einen geben, der garantiert, dass sein lock frei, nämlich
atomic_flag
.`
haben Sie die interlocked-Bibliothek in windows
http://msdn.microsoft.com/en-us/library/windows/desktop/ms683590(v=vs. 85).aspx
dieser als lock frei.
*il nur Zitat aus msdn als im kein großer Experte:
aber im Grunde, wenn die hardware premits es wird Ihnen eine Atomare operation, behandelt dieses "lockfree" sonst wird es eine Sperre um es zu verwenden, Speicher-Beeren-Technik
*
hier sind mehrere Varianten auf _InterlockedExchange, die variieren basierend auf den Datentypen, die Sie betreffen und ob die Prozessor-spezifische erwerben oder release-Semantik verwendet wird.
Während die _InterlockedExchange-Funktion arbeitet auf 32-bit-integer-Werte, _InterlockedExchange64 arbeitet auf 64-bit-integer-Werte.
Die _InterlockedExchange_acq und _InterlockedExchange64_acq intrinsische Funktionen sind die gleichen wie die entsprechenden Funktionen aus, ohne die _acq suffix, außer, dass der Betrieb erfolgt mit Erwerb der Semantik, was nützlich ist, beim Eintritt in einen kritischen Abschnitt.
Es gibt keine version dieser Funktion, die verwendet release-Semantik.
In Visual C++ 2005, diese Funktionen Verhalten sich, als lese-schreib-Speicher Barrieren. Weitere Informationen finden Sie unter _ReadWriteBarrier.
Diese Routinen sind nur als Interna.