Programação Assíncrona em C++: guia completo na prática
Programação assíncrona em C++ é uma abordagem que permite executar várias tarefas em paralelo, sem bloquear o fluxo principal de execução. Ao contrário do código síncrono, onde cada operação aguarda a conclusão da anterior, o código assíncrono pode iniciar operações longas (entrada/saída, requisições de rede, cálculos) e continuar executando outras tarefas enquanto essas operações são concluídas em segundo plano. Isso é fundamental para criar aplicações responsivas, especialmente na indústria de jogos, servidores web e sistemas de tempo real.
Neste artigo, abordaremos a programação assíncrona em C++ na prática: desde os mecanismos clássicos std::async e std::future até as modernas corrotinas C++20. Você aprenderá como usar essas ferramentas corretamente, evitar erros comuns e criar sistemas assíncronos eficientes.
1. Fundamentos da assincronia: std::async e std::future
A biblioteca padrão C++11 fornece uma maneira simples de iniciar tarefas assíncronas — a função std::async e a classe std::future. std::async inicia uma função em uma thread separada (ou pool de threads) e retorna um std::future, que contém o resultado da execução.
Exemplo 1: Inicialização básica de uma tarefa assí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 tarefa assíncrona..." << std::endl; std::future<int> result = std::async(std::launch::async, longComputation, 10);
// Enquanto a tarefa é executada, pode-se fazer outro trabalho std::cout << "Thread principal continua trabalhando..." << std::endl; std::this_thread::sleep_for(std::chrono::seconds(1));
// Obtendo o resultado (bloqueia a thread se a tarefa ainda não foi concluída) int value = result.get(); std::cout << "Resultado: " << value << std::endl; return 0;}Pontos importantes:
std::launch::asyncgarante a execução em uma thread separada.result.get()bloqueia a thread atual até obter o resultado.- Se
get()não for chamado, o destrutor destd::futurepode bloquear a execução até a conclusão da tarefa.
Exemplo 2: Tratamento de exceções em tarefas assí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 << "Exceção: " << e.what() << std::endl; } return 0;}2. Trabalho avançado com std::future e std::promise
std::promise é uma ferramenta para gerenciamento manual de resultados assíncronos. Você pode definir um valor ou exceção em uma thread e obtê-lo através de std::future em outra. Isso é útil para implementar padrões assíncronos personalizados, como transferência de dados entre threads.
Exemplo 3: Uso de std::promise para transferência de dados
#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("Dados prontos!");}
int main() { std::promise<std::string> promise; std::future<s