Asynchrones Programmieren in C++: Eine vollständige Anleitung
Moderne Anwendungen erfordern hohe Leistung und Reaktionsfähigkeit. Asynchrones Programmieren in C++ ermöglicht die Ausführung langwieriger Operationen (E/A, Netzwerkanfragen, komplexe Berechnungen), ohne den Hauptthread zu blockieren. In dieser Anleitung werden wir die wichtigsten C++-Mechanismen für asynchrone Arbeit behandeln: von klassischen std::thread und std::async bis hin zu modernen C++20-Koroutinen. Sie erfahren, wie Sie effizienten und sicheren asynchronen Code schreiben.
1. Grundlagen: std::thread und std::async
Beginnen wir mit den grundlegenden Werkzeugen. std::thread erstellt einen neuen Ausführungsthread, erfordert jedoch manuelle Verwaltung und Synchronisation. Eine bequemere Option ist std::async, das eine Funktion asynchron startet und ein std::future zum Abrufen des Ergebnisses zurückgibt.
#include <iostream>#include <future>#include <chrono>
int long_running_task(int input) { std::this_thread::sleep_for(std::chrono::seconds(2)); return input * 10;}
int main() { std::cout << "Starte asynchrone Aufgabe..."; std::future<int> result = std::async(std::launch::async, long_running_task, 5); // Etwas Nützliches tun, während die Aufgabe läuft std::cout << "Hauptthread arbeitet weiter..."; // Ergebnis abrufen (blockiert, falls die Aufgabe noch nicht abgeschlossen ist) int value = result.get(); std::cout << "Ergebnis: " << value << std::endl; return 0;}Wichtige Punkte:
- std::launch::async — garantiert den Start in einem separaten Thread.
- std::launch::deferred — verzögert die Ausführung bis zum Aufruf von
get(). - Die Methode
get()blockiert den Thread, bis das Ergebnis vorliegt.
2. Fortgeschrittene Arbeit: std::promise und std::future
Wenn Sie ein Ergebnis von einem Thread an einen anderen übergeben müssen, verwenden Sie das Paar std::promise und std::future. promise setzt den Wert, und future empfängt ihn. Dies ist besonders nützlich beim Thread-Pooling oder bei Aufgabenketten.
#include <iostream>#include <future>#include <thread>
void worker(std::promise<int> prom) { try { int result = 42; // Komplexe Berechnungen prom.set_value(result); } catch (...) { prom.set_exception(std::current_exception()); }}
int main() { std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread t(worker, std::move(prom)); // Ergebnis abrufen int value = fut.get(); std::cout << "Wert empfangen: " << value << std::endl; t.join(); return 0;}Wichtige Besonderheiten:
promisekann nicht kopiert, sondern nur verschoben werden.- Ausnahmen werden über
set_exceptionübergeben. futurekann nur einmal verwendet werden (einmaliger Abruf).
3. C++20-Koroutinen: Moderner Ansatz
Koroutinen (coroutines) sind ein leistungsstarkes Werkzeug, das in C++20 hinzugefügt wurde. Sie ermöglichen es, die Ausführung einer Funktion zu unterbrechen und wieder aufzunehmen, ohne den Thread zu blockieren. Dies ist ideal für asynchrone E/A-Operationen und Generatoren.
#include <iostream>#include <coroutine>#include <future>
struct SimpleTask { struct promise_type { SimpleTask get_return_object() { return {}; } std::suspend_never initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() { std::exit(1); } };};
SimpleTask async_operation() { std::cout << "Start der Koroutine";