Programação Assíncrona em C++: de std::async a Corrotinas
Programação assíncrona em C++ é uma ferramenta poderosa para criar aplicações de alto desempenho e responsivas. No mundo moderno, onde processadores multi-core se tornaram padrão, a capacidade de gerenciar tarefas paralelas de forma eficiente é uma habilidade obrigatória para todo desenvolvedor C++. Neste artigo, vamos explorar os conceitos-chave, começando pelos mecanismos básicos da biblioteca padrão e terminando com as corrotinas modernas do C++20.
Por que a programação assíncrona é necessária?
O principal objetivo da programação assíncrona é não bloquear o fluxo principal de execução do programa. Imagine que sua aplicação precisa baixar um arquivo da rede, processá-lo e mostrá-lo ao usuário. Se essas operações forem executadas de forma síncrona, a interface "congelará" durante o download. A abordagem assíncrona permite iniciar uma operação longa "em segundo plano", enquanto o fluxo principal continua processando a entrada do usuário.
Principais abordagens para assincronia em C++
Em C++, existem várias maneiras de implementar a execução assíncrona:
- Gerenciamento manual de threads (std::thread) — abordagem de baixo nível, mas flexível.
- std::async e std::future — mecanismo de alto nível para iniciar tarefas assíncronas.
- std::packaged_task e std::promise — para controle mais refinado.
- Corrotinas (C++20) — maneira moderna e elegante de escrever código assíncrono.
Usando std::async e std::future
Esta é a maneira mais simples de iniciar uma tarefa assíncrona. A função std::async retorna um objeto std::future, que conterá o resultado da execução.
#include <iostream>#include <future>#include <thread>#include <chrono>
int heavy_computation(int x) { std::this_thread::sleep_for(std::chrono::seconds(2)); return x * x;}
int main() { std::cout << "Iniciando tarefa assíncrona..."; std::future<int> result = std::async(std::launch::async, heavy_computation, 42); // Fazendo outra coisa enquanto a tarefa é executada std::cout << "O fluxo principal não está bloqueado!"; // Obtendo o resultado (bloqueia se a tarefa ainda não foi concluída) int value = result.get(); std::cout << "Resultado: " << value << ""; return 0;}A flag std::launch::async garante que a tarefa será executada em uma thread separada. Se não for especificada, a implementação pode adiar a execução até a chamada de .get().
Trabalhando com std::promise e std::packaged_task
Esses mecanismos fornecem mais controle. std::promise permite "prometer" um valor que será definido posteriormente, enquanto std::packaged_task envolve um objeto chamável em uma tarefa.
#include <iostream>#include <future>#include <thread>
void set_value(std::promise<int>&& prom) { std::this_thread::sleep_for(std::chrono::seconds(1)); prom.set_value(100);}
int main() { std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread t(set_value, std::move(prom)); std::cout << "Aguardando resultado..."; int value = fut.get(); std::cout << "Recebido: " << value << ""; t.join(); return 0;}Importante: std::promise e std::future são objetos move-only. Eles não podem ser copiados, apenas movidos.
Corrotinas em C++20: um novo nível de assincronia
Corrotinas (coroutines) são funções que podem pausar sua execução e retomá-la posteriormente. Elas permitem escrever código assíncrono como se fosse síncrono.