24.11
2020
std::async é uma função que constrói um std::future baseado em uma função, sem a necessidade de um std::promise, simplificando a utilização de processos assíncronos.
Vamos começar com um exemplo bem simples:
#include <future>
#include <iostream>
#include <list>
int main() {
int n = 10;
std::cout << "Generating first " << n << " prime numbers..." << std::endl;
auto listprimes = std::async(
std::launch::async,
[](unsigned n) {
std::list<unsigned> primes;
for (int num = 2; primes.size() != n; num++) {
int div;
for (div = 2; div < num; div++)
if (num % div == 0)
break;
if (div == num)
primes.push_back(num);
}
return primes;
},
n
);
auto primes = listprimes.get();
std::cout << "Primes generated: " << std::endl;
for (auto it = primes.begin(); it != primes.end(); it++) {
std::cout << *it << " ";
}
std::cout << std::endl;
}
O código acima calcula os n primeiros números primos. A função lambda faz o cálculo enquanto que a função main aguarda o processamento. A saída será a seguinte:
Generating first 10 prime numbers...
Primes generated:
2 3 5 7 11 13 17 19 23 29
Opcionalmente a função std::async aceita um parâmetro que define o tempo de execução da função. São duas opções para o tempo de execução:
- std::launch::async – cria uma std::thread assíncrona com base na função fornecida e executa o método std::thread::join() logo após a chamada do método std::future::get().
- std::launch::deferred – aguarda a chamada do método std::future::wait() ou std::future::get() para executar a função.
É possível ainda fazer um ou binário entre as duas opções (std::launch::async | std::launch::deferred), e o compilador irá selecionar qual o método mais apropriado conforme a implementação da biblioteca.
Um teste para ilustrar os dois casos.
#include <future>
#include <iostream>
#include <thread>
void countdown(unsigned id, unsigned n) {
for(; n--; ) {
std::cout << id << "." << n << " " << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
int main() {
int n = 4;
std::cout << "Countdown from " << n << " ..." << std::endl;
auto async1 = std::async(std::launch::async, countdown, 1, n);
auto async2 = std::async(std::launch::async, countdown, 2, n);
std::cout << "Waiting for countdown()...";
async1.wait();
async2.wait();
std::cout << "Countdown done" << std::endl << std::endl;
std::cout << "Countdown from " << n << " ..." << std::endl;
auto deferred3 = std::async(std::launch::deferred, countdown, 3, n);
auto deferred4 = std::async(std::launch::deferred, countdown, 4, n);
std::cout << "Waiting for countdown()...";
deferred3.wait();
deferred4.wait();
std::cout << "Countdown done" << std::endl;
}
São quatro contagens regressivas rodando em paralelo duas a duas. As duas primeiras são executadas utilizando std::launch::async, sendo executadas em paralelo. Já as duas últimas são executadas utilizando std::launch::deferred, e são executadas sequencialmente conforme deferred3.wait() e deferred4.wait() são chamadas. Confira a saída:
Countdown from 4 ...
Waiting for countdown()...1.3
2.3
1.2
2.2
1.1
2.1
2.0
1.0
Countdown done
Countdown from 4 ...
Waiting for countdown()...3.3
3.2
3.1
3.0
4.3
4.2
4.1
4.0
Countdown done