O cabeçalho <future>, introduzido no C++11, fornece mecanismos para transportar resultados assíncronos. Ele permite que tarefas concorrentes ou assíncronas transmitam valores de retorno ou exceções de forma segura, eliminando a necessidade de implementar manualmente locks, variáveis de condição ou callbacks.
Componentes Principais
| Classe/Função | Função | Complemento |
|---|---|---|
std::async |
Inicia uma tarefa assíncrona | Retorna std::future |
std::future<T> |
Recebe um resultado assíncrono único | async, packaged_task, promise |
std::promise<T> |
Define um resultado manualmente | Compartilha estado com future |
std::shared_future<T> |
Permite compartilhar um mesmo resultado entre múltiplas threads | Pode ser copiado |
std::packaged_task<R(Args...)> |
Encapsula um objeto invocável como uma tarefa assíncrona | Compartilha estado com future |
Exemplo Básico
1. Assincronicidade com std::async
#include <future>
#include <iostream>
int calcular() { return 42; }
int main() {
std::future<int> resultado = std::async(std::launch::async, calcular);
std::cout << resultado.get(); // Bloqueia até que o resultado esteja disponível
}
2. Enviando resultados manualmente com promise/future
std::promise<int> promessa;
std::future<int> futuro = promessa.get_future();
std::thread t([&promessa]{
promessa.set_value(100); // Define o resultado na thread
});
std::cout << futuro.get(); // A thread principal obtém 100
t.join();
Quando Utilizar
| Cenário | Uso Recomendado |
|---|---|
| Computação simples em segundo plano | std::async |
| Tarefas em pool de threads | packaged_task ou promise |
| Necessidade de múltiplos consumidores | shared_future |
Cuidados Importantes
future.get()pode ser chamado apenas uma vez; para múltiplas leituras, utilizeshared_future.- Destruir um
promisesem definir um valor resulta emstd::future_error. - Por padrão,
std::asyncpode executar de forma síncrona (dependente da implementação); especifiquestd::launch::asyncpara garantir execução concorrente.
Resumo
<future> permite "enviar tarefas" de forma similar a "enviar uma encomenda", recebendo os resultados de maneira segura sem a necessidade de locks ou variáveis de condição.
Função async
#include <iostream>
#include <future>
#include <thread>
int somar(int a, int b)
{
std::cout << "Executando somar!\n";
return a + b;
}
int main()
{
// Estratégia std::launch::async: cria uma nova thread para executar a função. O resultado é obtido via future.
// Estratégia std::launch::deferred: execução adiada (síncrona), a tarefa é executada ao chamar get().
std::future<int> resultado = std::async(std::launch::deferred, somar, 11, 22); // Chamada assíncrona não-bloqueante
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "--------------------------\n";
// std::future<int>::get() é usado para obter o resultado da tarefa assíncrona. Bloqueia se o resultado não estiver pronto.
std::cout << resultado.get() << std::endl;
return 0;
}
// Saída com deferred:
// --------------------------
// Executando somar!
// 33
// Saída com async:
// Executando somar!
// --------------------------
// 33
Diferenças de comportamento entre std::launch::deferred e std::launch::async:
| Estratégia | Thread | Momento da Execução | Ordem da Saída |
|---|---|---|---|
deferred |
Thread atual | Somente na primeira chamada a get() |
------------------ → Executando somar! |
async |
Nova thread | Imediatamente, de forma concorrente | Executando somar! → ------------------ |
Resumo
deferred= Sincronia adiada (execução preguiçosa);async= Assincronia real (execução imediata em segundo plano).
Promise
#include <iostream>
#include <future>
#include <thread>
#include <memory>
int somar(int a, int b)
{
std::cout << "Executando somar!\n";
return a + b;
}
int main()
{
// 1. Instanciar um objeto promise com o tipo de resultado esperado.
std::promise<int> promessa;
// 2. Obter o objeto future associado a essa promise.
std::future<int> futuro = promessa.get_future();
// 3. Em qualquer ponto, definir o valor na promise; ele poderá ser recuperado via future.
std::thread worker([&promessa](){
int resultado = somar(11, 22);
promessa.set_value(resultado);
});
std::cout << futuro.get() << std::endl;
worker.join();
return 0;
}
Fluxo Chave (correspondendo aos comentários)
| Passo | Código | Significado |
|---|---|---|
| 1️⃣ Criar a promessa | promise<int> promessa; |
Preparar uma "caixa" para o resultado |
| 2️⃣ Obter o cupom | future<int> futuro = promessa.get_future(); |
A thread principal obtém o "cupom de retirada" |
| 3️⃣ Produção assíncrona | thread worker([&promessa]{...}) |
A thread de trabalho calcula e coloca o resultado na caixa |
| 4️⃣ Bloqueio para retirada | futuro.get() |
A thread principal bloqueia de forma segura até que o resultado esteja pronto |
| 5️⃣ Limpar recursos | worker.join() |
Esperar que a thread de trabalho termine |
Erros Comuns
- Esquecer de chamar
join()→ A thread ainda está em execução quando o programa encerra, causandoterminate. - Chamar
get()múltiplas vezes →futuresó permite uma chamada aget(); para múltiplas leituras, utilizeshared_future. - Propagação de exceções → Se a thread de trabalho lançar uma exceção,
get()na thread principal a relançará.
Resumo
promise é responsável por "colocar", future por "retirar";
A combinação promise/future permite transferir resultados assíncronos para a thread principal com segurança e sem locks.
packaged_task
#include <iostream>
#include <future>
#include <thread>
#include <memory>
int multiplicar(int x, int y) {
std::cout << "Executando multiplicar!\n";
return x * y;
}
int main()
{
//1. Encapsular a tarefa
auto tarefa = std::make_shared<std::packaged_task<int(int, int)>>(multiplicar);
//2. Obter o objeto future associado à tarefa
std::future<int> futuro = tarefa->get_future();
std::thread worker([tarefa](){
(*tarefa)(5, 7);
});
//3. Obter o resultado
std::cout << futuro.get() << std::endl;
worker.join();
return 0;
}
| Passo | Código | Função |
|---|---|---|
| 1️⃣ Encapsular a tarefa | make_shared<packaged_task<int(int,int)>> |
Transformar uma função comum em uma tarefa executável de forma assíncrona |
| 2️⃣ Obter o resultado | future<int> futuro = tarefa->get_future(); |
Obter o "cupom de retirada" |
| 3️⃣ Execução assíncrona | thread worker([tarefa]{ (*tarefa)(5,7); }) |
Invocar efetivamente a tarefa dentro de uma thread |
| 4️⃣ Bloqueio para retirada | futuro.get() |
A thread principal bloqueia de forma segura até que o resultado esteja pronto |
Resultado da Execução
Executando multiplicar!
35
Pontos de Atenção
- Só é possível chamar
get()uma vez; para múltiplas leituras, utilizestd::shared_future. - Exceção na tarefa →
futuro.get()relançará a exceção. - Esquecer de chamar
worker.join()→ A thread ainda está ativa ao encerrar o programa, resultando emstd::terminate.
Resumo
packaged_task transforma um "objeto invocável" em uma "tarefa com retorno, executável de forma assíncrona". Combinado com future, implementa a transferência segura de resultados entre threads sem a necsesidade de locks.