c++ - ¿Cuándo usar promise en async o packaged_task?
asynchronous future (2)
¿Cuándo debería usar std::promise
en std::async
o std::packaged_task
? ¿Me puede dar ejemplos prácticos de cuándo usar cada uno de ellos?
std :: async
std::async
es una forma clara y fácil de obtener un std::future
, pero:
No siempre comienza un nuevo hilo; pase
std::launch::async
como primer parámetro para forzarlo.auto f = std::async( std::launch::async, func );
el
std::~future
destructor puede bloquearse hasta que termine el nuevo subprocesoauto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); }; { auto f = std::async( std::launch::async, sleep, 5 ); }
Normalmente, esperamos que solo los
.get()
o.wait()
, pero parastd::future
devuelto porstd::async
destructor también puedan bloquearse, así que tenga cuidado de no bloquear el hilo principal simplemente olvidándolo.si
std::future
se almacena en un objeto de vida temporal, la llamadastd::async
se bloqueará in situ, por lo que el siguiente bloque demorará 10 segundos si elimina las inicializacionesauto f =
auto sleep = [](int s) { std::this_thread::sleep_for(std::chrono::seconds(s)); }; { auto f1 = std::async( std::launch::async, sleep, 5 ); auto f2 = std::async( std::launch::async, sleep, 5 ); }
std :: packaged_task
std::packaged_task
por sí mismo no tiene nada que ver con los hilos: es solo un functor y un futuro relacionado. Considera lo siguiente:
auto task = [](int i) { std::this_thread::sleep_for(std::chrono::seconds(5)); return i+100; };
std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
package(1);
std::cout << f.get() << "/n";
Aquí simplemente ejecutamos la tarea por package(1)
, y después de que devuelve f
está listo así que no hay bloqueo en .get()
.
Pero: una característica hace que packaged_task sea muy útil para los hilos. En lugar de solo una función, puede inicializar std::thread
con un packaged_task. Considere lo siguiente: packaged_task es una forma realmente agradable de obtener futuro:
std::packaged_task< int(int) > package{ task };
std::future<int> f = package.get_future();
std::thread t { std::move(package), 5 };
std::cout << f.get() << "/n"; //block here until t finishes
t.join();
std::packaged_task
packaged_task no se puede copiar, por lo que se mueve a una nueva tarea con std::move
.
std :: promesa
std::promise
es un mecanismo poderoso, por ejemplo, puede pasar un valor a un nuevo hilo sin necesidad de ningún mecanismo de sincronización adicional.
auto task = [](std::future<int> i) {
std::cout << i.get() << std::flush;
};
std::promise<int> p;
std::thread t{ task, p.get_future() };
std::this_thread::sleep_for(std::chrono::seconds(5));
p.set_value(5);
t.join();
Nuevo hilo esperará por nosotros en .get()
Entonces, en general, respondiendo su pregunta:
- use
std::async
solo para cosas simples, por ejemplo, para hacer llamadas no bloqueantes, pero tenga en cuenta los comentarios sobre el bloqueo anterior. use
std::packaged_task
para obtener futuro fácilmente y ejecútelo como un hilo separadostd::thread{ std::move(package), param }.detach();
o
std::thread t { std::move(package), param };
use
std::promise
cuando necesite más control sobre el futuro.
Ver también std::shared_future
y sobre pasar excepciones entre hilos std::promise::set_exception
Una promesa se usa para almacenar un valor que se calculó utilizando, por ejemplo, un std :: async. Ver std::promise
Me imagino que te preguntas acerca de la diferencia entre std :: packaged_task y std :: async (en el enfoque más común, std :: async inicia un hilo por separado NOW para ejecutar la función / lambda / etc con un cálculo (probable) costoso. std :: packaged_task se utiliza para ajustar una función / lambda / etc con los valores actuales de los argumentos para que luego pueda ejecutarlo, ya sea sincrónicamente o en un hilo separado).
Tanto std :: packaged_task como std :: async proporcionan un std :: future que contendrá el RESULTADO de la función envuelta / lambda / etc una vez ejecutada. Internamente, std :: future usa una promesa estándar para mantener ese resultado.