19.11
2020
Promises fazem parte das novas formas de gerenciar produção e consumo de dados entre threads, basicamente sincronizando a execução de uma thread de geração ou recuperação de dados, com a thread que consome esses mesmos dados.
Vamos começar com uma analogia. Felipe está esperando uma correspondência que Pedro vai trazer; ao invés de ficar regularmente verificando regularmente a caixa de correio, Felipe prefere colocar uma sineta na caixa de correio enquanto dorme ao seu lado. Quando Pedro conseguir trazer a carta a campainha vai tocar e Felipe pode acordar para poder abrir a correspondência.
Pedro é uma Promise, enquanto que Felipe é um Future. Em C++ são Promise e Future são classes que são utilizadas para se comunicar entre si e indicar que um determinado dado está pronto para ser consumido. Um exemplo bem básico:
#include <iostream>
#include <future>
#include <thread>
int main() {
std::promise<unsigned long long> provider;
std::future<unsigned long long> consumer = provider.get_future();
std::thread fat(
[&provider](unsigned n) {
unsigned long long r = 1;
for (int ct = n; ct--; )
r *= ct + 1;
std::cout << "Factorial ready" << std::endl;
provider.set_value(r);
},
20
);
std::cout << "Waiting for factorial" << std::endl;
long long r = consumer.get();
std::cout << "r: " << r << std::endl;
fat.join();
}
Que entregará a saída:
Waiting for factorial
Factorial ready
r: 2432902008176640000
provider é um objeto std::promise que está associada à uma thread que calcula o fatorial, e consumer é um objeto std::future que consome o fatorial gerado pela thread.
Existe também a classe packaged_task, que basicamente é uma promise que contém sua própria função, simplificando a implementação:
#include <future>
#include <iostream>
int main() {
std::packaged_task<void(unsigned)> waittimer(
[](unsigned secs) {
for (int ct = secs; ct--; )
std::this_thread::sleep_for(std::chrono::seconds(1));
}
);
std::future<void> wait = waittimer.get_future();
std::thread waitthread(std::move(waittimer), 5);
std::cout << "Calculating factorial..." << std::endl;
unsigned long long fat = 1;
for (int ct = 10; ct--; )
fat *= ct + 1;
std::cout << "Factorial ready: " << fat << std::endl;
std::cout << "Waiting until 5 secs..." << std::endl;
wait.get();
std::cout << "Done!" << std::endl;
waitthread.join();
}
E a saída será:
Calculating factorial...
Factorial ready: 3628800
Waiting until 5 secs...
Done!
Note a necessidade de usar std::move para evitar a criação de uma nova instância da promise, o que poderia causar problemas.
Parece simples, e é simples, pelo menos pro enquanto, porque as coisas podem ficar mais complexas. Mas para deixar as coisas mais complexas vamos precisar de outros posts.