Asynchrone Programmierung in C++: vollständiger Leitfaden in der Praxis
Asynchrone Programmierung in C++ ist ein Ansatz, der es ermöglicht, mehrere Aufgaben parallel auszuführen, ohne den Hauptausführungs-Thread zu blockieren. Im Gegensatz zu synchronem Code, bei dem jeder Vorgang auf den Abschluss des vorherigen wartet, kann asynchroner Code langwierige Operationen (Eingabe/Ausgabe, Netzwerkanfragen, Berechnungen) initiieren und mit der Ausführung anderer Aufgaben fortfahren, während diese Operationen im Hintergrund abgeschlossen werden. Dies ist entscheidend für die Erstellung reaktionsfähiger Anwendungen, insbesondere in der Spieleindustrie, bei Webservern und in Echtzeitsystemen.
In diesem Artikel analysieren wir die asynchrone Programmierung in C++ in der Praxis: von den klassischen Mechanismen std::async und std::future bis hin zu modernen Coroutinen in C++20. Sie erfahren, wie Sie diese Werkzeuge richtig einsetzen, typische Fehler vermeiden und effiziente asynchrone Systeme erstellen.
1. Grundlagen der Asynchronität: std::async und std::future
Die Standardbibliothek von C++11 bietet eine einfache Möglichkeit, asynchrone Aufgaben zu starten – die Funktion std::async und die Klasse std::future. std::async startet eine Funktion in einem separaten Thread (oder Thread-Pool) und gibt ein std::future zurück, das das Ergebnis der Ausführung enthält.
Beispiel 1: Grundlegender Start einer asynchronen Aufgabe
#include <iostream>#include <future>#include <thread>#include <chrono>
int longComputation(int n) { std::this_thread::sleep_for(std::chrono::seconds(2)); return n * n;}
int main() { std::cout << "Starte asynchrone Aufgabe..." << std::endl; std::future<int> result = std::async(std::launch::async, longComputation, 10);
// Während die Aufgabe läuft, kann andere Arbeit erledigt werden std::cout << "Haupt-Thread arbeitet weiter..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1));
// Ergebnis abrufen (blockiert den Thread, wenn die Aufgabe noch nicht abgeschlossen ist) int value = result.get(); std::cout << "Ergebnis: " << value << std::endl; return 0;}Wichtige Punkte:
std::launch::asyncgarantiert den Start in einem separaten Thread.result.get()blockiert den aktuellen Thread, bis das Ergebnis vorliegt.- Wenn
get()nicht aufgerufen wird, kann der Destruktor vonstd::futuredie Ausführung blockieren, bis die Aufgabe abgeschlossen ist.
Beispiel 2: Ausnahmebehandlung in asynchronen Aufgaben
#include <iostream>#include <future>#include <stdexcept>
int riskyFunction(int x) { if (x < 0) throw std::runtime_error("Negative Zahl!"); return x * 2;}
int main() { std::future<int> future = std::async(std::launch::async, riskyFunction, -5); try { int result = future.get(); std::cout << "Ergebnis: " << result << std::endl; } catch (const std::exception& e) { std::cout << "Ausnahme: " << e.what() << std::endl; } return 0;}2. Fortgeschrittene Arbeit mit std::future und std::promise
std::promise ist ein Werkzeug zur manuellen Verwaltung asynchroner Ergebnisse. Sie können einen Wert oder eine Ausnahme in einem Thread setzen und ihn über std::future in einem anderen abrufen. Dies ist nützlich für die Implementierung eigener asynchroner Muster, z. B. zur Datenübertragung zwischen Threads.
Beispiel 3: Verwendung von std::promise zur Datenübertragung
#include <iostream>#include <future>#include <thread>
void producer(std::promise<std::string> prom) { std::this_thread::sleep_for(std::chrono::seconds(1)); prom.set_value("Daten bereit!");}
int main() { std::promise<std::string> promise; std::future<s