Parallelität und Multithreading in C++: Fortgeschrittenes Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Parallelität und Multithreading in C++: Fortgeschrittenes Niveau

\n\n

Moderne Anwendungen erfordern maximale Leistung, und Multithreading in C++ ist eines der Schlüsselwerkzeuge, um dies zu erreichen. Allerdings führen die grundlegenden Mechanismen (std::thread, std::mutex) oft zu Blockaden, Datenrennen und Effizienzeinbußen. In diesem Artikel betrachten wir fortgeschrittene Techniken für die Arbeit mit Parallelität in C++: von atomaren Operationen bis hin zu Lock-freien Datenstrukturen und asynchronen Aufgaben.

\n\n

1. Atomare Operationen und Memory Ordering

\n\n

Klassische Mutexe sind ein schwerfälliger Mechanismus. Für einfache Zähler oder Flags ist es besser, std::atomic zu verwenden. Es garantiert die Atomarität von Lese-/Schreibvorgängen ohne Sperren. Auf fortgeschrittenem Niveau ist es jedoch wichtig, das Memory Ordering zu verstehen – das C++-Speichermodell, das die Sichtbarkeit von Änderungen zwischen Threads definiert.

\n\n

Die wichtigsten Speicherordnungen:

\n
    \n
  • memory_order_relaxed – minimale Garantien, nur Atomarität.
  • \n
  • memory_order_consume – selten verwendet, datenabhängig.
  • \n
  • memory_order_acquire – alle nachfolgenden Lese-/Schreibvorgänge können nicht vor dieser Operation umgeordnet werden.
  • \n
  • memory_order_release – alle vorherigen Schreibvorgänge werden nach dieser Operation für andere Threads sichtbar.
  • \n
  • memory_order_acq_rel – Kombination aus Acquire und Release.
  • \n
  • memory_order_seq_cst – die strengste, globale sequenzielle Konsistenz (Standard).
  • \n
\n\n

Anwendungsbeispiel:

\n
#include <atomic>\n#include <thread>\n#include <cassert>\n\nstd::atomic<bool> ready{false};\nint data = 0;\n\nvoid producer() {\n    data = 42;                                   // Daten schreiben\n    ready.store(true, std::memory_order_release); // Flag veröffentlichen\n}\n\nvoid consumer() {\n    while (!ready.load(std::memory_order_acquire)) // auf Flag warten\n        ;\n    assert(data == 42); // Garantiert data == 42 sehen\n}\n\nint main() {\n    std::thread t1(producer);\n    std::thread t2(consumer);\n    t1.join();\n    t2.join();\n    return 0;\n}
\n\n

Hier stellen release und acquire sicher, dass das Schreiben von data vor dem Setzen des Flags erfolgt und das Lesen danach. Dies ist schneller als ein Mutex.

\n\n

2. Lock-freie Datenstrukturen

\n\n

Lock-freie Algorithmen vermeiden Sperren durch die Verwendung von atomaren Operationen und CAS (Compare-And-Swap). Ein klassisches Beispiel ist ein Lock-freier Stack auf Basis einer einfach verketteten Liste.

\n\n

Implementierung eines Lock-freien Stacks:

\n
#include <atomic>\n#include <memory>\n\ntemplate<typename T>\nclass LockFreeStack {\nprivate:\n    struct Node {\n        T data;\n        Node* next;\n        Node(const T& val) : data(val), next(nullptr) {}\n    };\n    std::atomic<Node*> head{nullptr};\n\npublic:\n    void push(const T& val) {\n        Node* new_node = new Node(val);\n        new_node->next = head.load(std::memory_order_relaxed);\n        while (!head.compare_exchange_weak(\n            new_node->next, new_node,\n            std::memory_order_release,\n            std::memory_order_relaxed)) {\n            // Schleife, bis CAS erfolgreich ist\n        }\n    }\n\n    bool pop(T& result) {\n        Node* old_head = head.load(std::memory_order_relaxed);\n        while (old_head != nullptr &&\n               !head.compare_exchange_weak(\n                   old_head, old_head->next,\n                   std::memory_order_acquire,\n                   std::memory_order_relaxed)) {\n            // warten\n        }\n        if (old_head == nullptr) return false;\n        result = old_head->data;\n        // Achtung: In echtem Code wird ein Garbage Collector oder Hazard Pointer benötigt\n        delete old_head;\n        return true;\n    }\n};
\n\n

Wichtige Hinweise:

\n
    \n

Blogs

Buchempfehlungen