C ++ 26: Vereinfachte Implementierung Eines Lock-Freien Stacks
2 mins read

C ++ 26: Vereinfachte Implementierung Eines Lock-Freien Stacks

Nach der Theorie Zur Lock-Freien Programmerung in Meinem Letzten Beitrag Geh es Nun Um Die Practice Umsetzung.

Anzeige





Rainer Grimm ist Seit Vielen Jahren Als Softwareearchitekt, Team-Dund Schulungsliter Dense. Your Schreibt Gerne article zu the programmiersprachen C ++, Python und Haskell, Spricht Auch Auch Gerne und Häufig Auf Fachkonferenzen. AUF Seinem Blog Modernnes C ++ Rejuvenesy is Sich Intensive Mit Seiner Leidenschaft C ++.



Von außen Betrachtet is instead of AUFUNDE (Die Anwendung) Dafür Verantwortlich, Die Date Zu Schützen. Von Innen Betrachtet is die data structure Dafür Verantwortlich, Sich Selbst Zu Schützen. Eine Data Structure, Die Sich Selbst Schützt, Sodass Kein Data Race Airmen Kann, Wird Als Thread-Sicher Bezeichnet.

Zunächst stormy sich die frage, wie man beim entwurf einer concurrency-date structure Vorgehen Sollte.

  • Lock strategy: Sollte die data structure Grob- Oder Feinköniges Locking Unterstützen Oder Lock-Frei Sein? Grobköriges Locking ist Zwar Einfacher Zu Implementier, Führt Aber Zu conflict. Eine Feinkönte Oder Lock-Freie Implementierung ist Viel Aspruchsvoller. Zunächst Einmal: Wie ist Grobköriges Locking Zu Verstehen? Grobkörnes Locking Bedeutet, Dass Die Date Structure Zu Einem Bestimment Zeit Point Nur von Einem Thread Verway Wird.
  • Die granularitet der schnittstelle: Je Mächtiger Die Schnittstelle der thread-Sicheren Data structure ist, Da Schwieriger Wird es, über Ihre Gleichzeitige Nutzung Nachzudenken.
  • Typiske’s Nutzung Muster: Wenn Leser Deine Data Structure Haupttsächlich Verwenden, Solten du Sie Nicht Für Schreiber Optimieren.
  • Vermeiden von Schlupflöchern: Gib die internal deiner data structure Nicht a customer Weiter.
  • Competition: Come Gleichzeitige Clientanfragen in deiner dent structure Häufig were?
  • Skalierbareit: Wie ist die performanz deiner data structure, wenn die anzahl der Gleichzeitigen clients zunimt der die data structure limnzt ist?
  • Invariant: Welche Invariante Muss für Deine Data Structure Bei Der Verwendung Gelten?
  • Ausnahmen: Was Soll Passier, Wenn Eine Ausnahme Auftritt?

Natürlich Sind Diese überlegungen Voneinander Abheads. So can beispielsweise die Verwendung einer grobkörtigen Lock strategy die competitiveness um die dating structure Erhöh und die Skalierborte beeintemigigen.

Zunächst Einmal: Wie Sieht Ein Stack Aus?



std::stack Folt them Livo-Prinzip (last in-first). Einstack staThere that headstack> Leg -care, hate drei member function: mit sta.push(e)Jug man ein neues elements e oben auf it stack einfügen, es mit sta.pop() von Oben Entfern und MIT sta.top() Darauf Verweisen. There, Unterstützt left Die Vergleichs operator und Kennt Seine Größe.

#include 
...
std::stack myStack;

std::cout << myStack.empty() << '\n'; // true
std::cout << myStack.size() << '\n'; // 0

myStack.push(1);
myStack.push(2);
myStack.push(3);

std::cout << myStack.top() << '\n'; // 3
while (!myStack.empty()){
    std::cout << myStack.top() << " ";
    myStack.pop();
} // 3 2 1

std::cout << myStack.empty() << '\n'; // true
std::cout << myStack.size() << '\n'; // 0

Begnen wir nun mit there implementerung eines lock-free stacks.

In Meiner Verinfachten Irpementierung Beginner Ich Mit Der push-Member function. Zunächst Möchte Ich Verankaulic, Wie Ein Neuer Knoten Zu Einer Einfach Worksetten List Hinzugefügt Wird. head ist der zeiger auf the Ersten Knoten in Der Einfach the Listen.







Jeder Knoten in Der Einfach The Workset Listen Hat Zwei Attribute: Seinen Wert T Und Den Zeiger next. next Verweist auf das nächste element in der einfach The workshop list. Nur there attached verweist auf it nullptr. Das hinzufügen eines neuen knotens zu the date ist unbackiziert. Erstelle Einen Neuen Knoten und Lasse Den next-Seiger auf the vorhrigen kopf verweisen. Bisher ist der Neue Knot Nicht Zugändlich. Schließlich Wird der Neue Knoten Zum Neuen Kopf und Schließt the push-Vorgang AB.

DAS FOODING BEISPIEL ZEIGT DIE Lås-Freie Implementation Eines simultaneous stacks:

// lockFreeStackPush.cpp

#include 
#include 

template
class LockFreeStackPush {
 private:
    struct Node {
        T data;
        Node* next;
        Node(T d): data(d), next(nullptr) {}
    };
    std::atomic head;
 public:
    LockFreeStackPush() = default;
    LockFreeStackPush(const LockFreeStackPush&) = delete;
    LockFreeStackPush& operator= (const LockFreeStackPush&)
      = delete;
   
    void push(T val) {
        Node* const newNode = new Node(val);                // 1
        newNode->next = head.load();                        // 2
        while( !head.compare_exchange_strong(newNode->next,
                                            newNode) );     // 3
    }
};
   
int main(){

    LockFreeStackPush lockFreeStack;
    lockFreeStack.push(5);
    
    LockFreeStackPush lockFreeStack2;
    lockFreeStack2.push(5.5);
    
    LockFreeStackPush<:string> lockFreeStack3;
    lockFreeStack3.push("hello");

}

Ich Möchte Die Enscheidende Member Function push The analysis. Sie Erstally the neuen knot (1), Passt Seinen Nächsten Zeiger and the alta Kopf an und macht the neuen knot in the Einer late Cas-Oparation zum neuen kopf (3). Eine Cas-Oparation Bietet in Einem Atomaren Schritt Eine Vergleichs- und Tauschoperation.

Der AUFruf newNode->next = head.load() Lädt that alten Wert von head. Wenn der Geladene Wert newNode->next Immer Noch Derselst ist Wie head I (3), Wird head Auf newNode actualisiert und der auFruf head.compare_exchange_strong gibt true Zurück. Wenn Nicht, Gibt der AUFRFUL false Zurück und die while-Schleife Wird Ausgeführt, BIS der AuFruf true Zurückgibt. head.compare_exchange_strong gibt false Zurück, Wenn Ein Anderer thread Inzwisken Einen Neuen Knoten Zum Stack Hinzugefügt Hat.

Die Zeilen (2) und (3) im Codebeispiel image Eine Art atomare Transakion. Zuerst Wird Eine Mopafnahme der Datenstructure erstally (2), Dann Wird Versucht, Die Transakion Zu Veröffentlichen (3). Wenn Die Mopaufnahme Nicht Mehr Gültig Ist, Wird Ein Rollback Durchgeführt Und es Wird Erneut Versucht.

Die Heutige Vereinfachte Stack-Impementierung Dient Mir Als Constitution für Zukünftige Stacks.


(Rme)