Programmation asynchrone en C++ : guide complet
Les applications modernes nécessitent des performances élevées et une réactivité optimale. La programmation asynchrone en C++ permet d'exécuter des opérations longues (entrées-sorties, requêtes réseau, calculs complexes) sans bloquer le thread principal. Dans ce guide, nous analyserons les mécanismes clés de C++ pour le travail asynchrone : des classiques std::thread et std::async aux coroutines modernes de C++20. Vous apprendrez à écrire du code asynchrone efficace et sécurisé.
1. Bases : std::thread et std::async
Commençons par les outils de base. std::thread crée un nouveau thread d'exécution, mais nécessite une gestion et une synchronisation manuelles. Une option plus pratique est std::async, qui lance une fonction de manière asynchrone et retourne un std::future pour obtenir le résultat.
#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 << "Lancement de la tâche asynchrone..."; std::future<int> result = std::async(std::launch::async, long_running_task, 5); // Faire quelque chose d'utile pendant que la tâche s'exécute std::cout << "Le thread principal continue son travail..."; // Obtenir le résultat (bloque si la tâche n'est pas encore terminée) int value = result.get(); std::cout << "Résultat : " << value << std::endl; return 0;}Points clés :
- std::launch::async — garantit l'exécution dans un thread séparé.
- std::launch::deferred — reporte l'exécution jusqu'à l'appel de
get(). - La méthode
get()bloque le thread jusqu'à l'obtention du résultat.
2. Travail avancé : std::promise et std::future
Lorsque vous devez transmettre un résultat d'un thread à un autre, utilisez la paire std::promise et std::future. promise définit la valeur, et future la reçoit. C'est particulièrement utile pour le pooling de threads ou les chaînes de tâches.
#include <iostream>#include <future>#include <thread>
void worker(std::promise<int> prom) { try { int result = 42; // Calculs complexes 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)); // Obtenir le résultat int value = fut.get(); std::cout << "Valeur reçue : " << value << std::endl; t.join(); return 0;}Caractéristiques importantes :
promisene peut pas être copié, seulement déplacé.- Les exceptions sont transmises via
set_exception. futurene peut être utilisé qu'une seule fois (récupération unique).
3. Coroutines C++20 : approche moderne
Les coroutines (coroutines) sont un outil puissant ajouté en C++20. Elles permettent de suspendre et de reprendre l'exécution d'une fonction sans bloquer le thread. C'est idéal pour les opérations d'entrée-sortie asynchrones et les générateurs.
#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 << "Début de la coroutine";