Programación asíncrona en C++: guía completa en la práctica
La programación asíncrona en C++ es un enfoque que permite ejecutar múltiples tareas en paralelo sin bloquear el hilo principal de ejecución. A diferencia del código síncrono, donde cada operación espera la finalización de la anterior, el código asíncrono puede iniciar operaciones largas (entrada/salida, solicitudes de red, cálculos) y continuar ejecutando otras tareas mientras estas operaciones se completan en segundo plano. Esto es fundamental para crear aplicaciones receptivas, especialmente en la industria de los videojuegos, servidores web y sistemas en tiempo real.
En este artículo, analizaremos la programación asíncrona en C++ en la práctica: desde los mecanismos clásicos std::async y std::future hasta las corrutinas modernas de C++20. Aprenderá cómo usar correctamente estas herramientas, evitar errores típicos y crear sistemas asíncronos eficientes.
1. Fundamentos de la asincronía: std::async y std::future
La biblioteca estándar de C++11 proporciona una forma sencilla de lanzar tareas asíncronas: la función std::async y la clase std::future. std::async ejecuta una función en un hilo separado (o grupo de hilos) y devuelve un std::future que contiene el resultado de la ejecución.
Ejemplo 1: Inicio básico de una tarea asíncrona
#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 << "Iniciando tarea asíncrona..." << std::endl; std::future<int> result = std::async(std::launch::async, longComputation, 10);
// Mientras la tarea se ejecuta, se puede realizar otro trabajo std::cout << "El hilo principal continúa trabajando..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1));
// Obteniendo el resultado (bloquea el hilo si la tarea aún no ha terminado) int value = result.get(); std::cout << "Resultado: " << value << std::endl; return 0;}Puntos importantes:
std::launch::asyncgarantiza la ejecución en un hilo separado.result.get()bloquea el hilo actual hasta obtener el resultado.- Si no se llama a
get(), el destructor destd::futurepuede bloquear la ejecución hasta que la tarea finalice.
Ejemplo 2: Manejo de excepciones en tareas asíncronas
#include <iostream>#include <future>#include <stdexcept>
int riskyFunction(int x) { if (x < 0) throw std::runtime_error("¡Número negativo!"); return x * 2;}
int main() { std::future<int> future = std::async(std::launch::async, riskyFunction, -5); try { int result = future.get(); std::cout << "Resultado: " << result << std::endl; } catch (const std::exception& e) { std::cout << "Excepción: " << e.what() << std::endl; } return 0;}2. Trabajo avanzado con std::future y std::promise
std::promise es una herramienta para la gestión manual de resultados asíncronos. Puede establecer un valor o una excepción en un hilo y obtenerlo a través de std::future en otro. Esto es útil para implementar patrones asíncronos personalizados, por ejemplo, para transferir datos entre hilos.
Ejemplo 3: Uso de std::promise para transferir datos
#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("¡Datos listos!");}
int main() { std::promise<std::string> promise; std::future<s