c++ asynchronous promise future packaged-task

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 subproceso

    auto 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 para std::future devuelto por std::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 llamada std::async se bloqueará in situ, por lo que el siguiente bloque demorará 10 segundos si elimina las inicializaciones auto 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 separado

    std::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.