Programación asíncrona en C++: guía completa
Las aplicaciones modernas requieren alto rendimiento y capacidad de respuesta. La programación asíncrona en C++ permite ejecutar operaciones largas (entrada/salida, solicitudes de red, cálculos complejos) sin bloquear el hilo principal. En esta guía, analizaremos los mecanismos clave de C++ para el trabajo asíncrono: desde los clásicos std::thread y std::async hasta las corrutinas modernas de C++20. Aprenderá a escribir código asíncrono eficiente y seguro.
1. Fundamentos: std::thread y std::async
Comencemos con las herramientas básicas. std::thread crea un nuevo hilo de ejecución, pero requiere gestión manual y sincronización. Una opción más conveniente es std::async, que ejecuta una función de forma asíncrona y devuelve un std::future para obtener el resultado.
#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 << "Iniciando tarea asíncrona..."; std::future<int> result = std::async(std::launch::async, long_running_task, 5); // Hacemos algo útil mientras la tarea se ejecuta std::cout << "El hilo principal continúa trabajando..."; // Obtenemos el resultado (se bloquea si la tarea aún no ha terminado) int value = result.get(); std::cout << "Resultado: " << value << std::endl; return 0;}Puntos clave:
- std::launch::async — garantiza la ejecución en un hilo separado.
- std::launch::deferred — aplaza la ejecución hasta la llamada a
get(). - El método
get()bloquea el hilo hasta obtener el resultado.
2. Trabajo avanzado: std::promise y std::future
Cuando necesite transferir un resultado de un hilo a otro, use el par std::promise y std::future. promise establece el valor y future lo recibe. Esto es especialmente útil en agrupaciones de hilos o cadenas de tareas.
#include <iostream>#include <future>#include <thread>
void worker(std::promise<int> prom) { try { int result = 42; // Cálculos complejos 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)); // Obtenemos el resultado int value = fut.get(); std::cout << "Valor recibido: " << value << std::endl; t.join(); return 0;}Características importantes:
promiseno se puede copiar, solo mover.- Las excepciones se transmiten a través de
set_exception. futuresolo se puede usar una vez (obtención única).
3. Corrutinas de C++20: enfoque moderno
Las corrutinas (coroutines) son una herramienta poderosa añadida en C++20. Permiten pausar y reanudar la ejecución de una función sin bloquear el hilo. Esto es ideal para operaciones asíncronas de entrada/salida y generadores.
#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 << "Inicio de la corrutina";