tutorial thread libreria ejemplo c++ c++11 stdthread

c++ - libreria - ¿Cuándo es una buena idea usar std:: promise sobre los otros mecanismos std:: thread?



thread c++11 (2)

Estoy tratando de establecer algunas heurísticas para ayudarme a decidir la clase std::thread apropiada para usar.

Según tengo entendido, desde el nivel más alto (el más simple de usar, pero el menos flexible) hasta el nivel más bajo, tenemos:

  1. std :: async con / std :: future (std :: shared_future) (cuando se desea ejecutar en un async de subproceso productor desechable de una sola vez)
  2. std :: packaged_task (cuando desea asignar un productor, pero aplazar la llamada al hilo)
  3. std :: promesa (???)

Creo que tengo una idea decente de cuándo usar los dos primeros, pero todavía no estoy claro acerca de std::promise .

std::future junto con una llamada std::async , transforma efectivamente una devolución de llamada / functor / lambda en una llamada asíncrona (que devuelve inmediatamente, por definición). Un consumidor singular puede llamar a std::future::get() , una llamada de bloqueo, para recuperar sus resultados.

std::shared_future es solo una versión que permite múltiples consumidores.

Si desea vincular un valor std::future con una devolución de llamada del productor, pero desea diferir la invocación real a un momento posterior (cuando asocie la tarea a un subproceso de generación) , std::packaged_task es la opción correcta. Pero ahora, dado que std::future correspondiente a std::package_task podría, en el caso general, ser accedido por varios subprocesos, es posible que tengamos que tener cuidado de usar un std::mutex . Tenga en cuenta que con std::async , en el primer caso, no tenemos que preocuparnos por el bloqueo.

Después de haber leído algunos enlaces interesantes sobre promesa , creo que entiendo sus mecanismos y cómo configurarlos, pero mi pregunta es: ¿ cuándo elegirías usar una promesa sobre los otros tres?

Estoy buscando más una respuesta a nivel de aplicación, como una regla de oro (llene la ??? en 3. arriba), a diferencia de la respuesta en el enlace (por ejemplo, use std :: prometer implementar alguna biblioteca) mecanismo), por lo que puedo explicar más fácilmente cómo elegir la clase adecuada para un usuario principiante de std::thread .

En otras palabras, sería bueno tener un ejemplo útil de lo que puedo hacer con una std::promise que no se puede hacer con los otros mecanismos.

RESPONDER

A std::future es una bestia extraña: en general, no puede modificar su valor directamente.

Tres productores que pueden modificar su valor son:

  1. std::async través de una devolución de llamada asíncrona, que devolverá una instancia std::future .
  2. std::packaged_task , que, cuando se pasa a un hilo, invocará su devolución de llamada, actualizando así la instancia std::future asociada con ese std::packaged_task . Este mecanismo permite la vinculación temprana de un productor, pero una invocación posterior.
  3. std::promise , que le permite modificar su std::future asociado a través de su llamada set_value() . Con este control directo sobre la mutación de un std::future , debemos asegurarnos de que el diseño sea seguro para subprocesos si hay varios productores (use std::mutex según sea necesario).

Creo que la respuesta de SethCarnegie :

Una manera fácil de pensar es que puede establecer un futuro devolviendo un valor o haciendo una promesa. el futuro no tiene un método fijo; Esa funcionalidad es provista por promesa.

Ayuda a aclarar cuándo usar una promesa. Pero debemos tener en cuenta que un std::mutex puede ser necesario, ya que la promesa puede ser accesible desde diferentes hilos, dependiendo del uso.

Además, la respuesta de David''s Rodriguez también es excelente:

El extremo consumidor del canal de comunicación usaría un std :: future para consumir el dato del estado compartido, mientras que el subproceso productor usaría un std :: promete escribir en el estado compartido.

Pero como alternativa, ¿por qué no simplemente usar un std::mutex en un contenedor de resultados de stl, y un hilo o un grupo de subprocesos de productores para actuar sobre el contenedor? ¿Qué significa usar std::promise , en cambio, comprarme, además de una legibilidad adicional frente a un contenedor de resultados de stl?

El control parece ser mejor en la versión std::promise :

  1. wait () bloqueará un futuro dado hasta que se produzca el resultado
  2. Si solo hay un hilo productor, no es necesario un mutex.

La siguiente prueba de Google pasa tanto a helgrind como a drd, confirmando que con un solo productor y con el uso de wait (), no se necesita un mutex.

PRUEBA

static unsigned MapFunc( std::string const& str ) { if ( str=="one" ) return 1u; if ( str=="two" ) return 2u; return 0u; } TEST( Test_future, Try_promise ) { typedef std::map<std::string,std::promise<unsigned>> MAP; MAP my_map; std::future<unsigned> f1 = my_map["one"].get_future(); std::future<unsigned> f2 = my_map["two"].get_future(); std::thread{ [ ]( MAP& m ) { m["one"].set_value( MapFunc( "one" )); m["two"].set_value( MapFunc( "two" )); }, std::ref( my_map ) }.detach(); f1.wait(); f2.wait(); EXPECT_EQ( 1u, f1.get() ); EXPECT_EQ( 2u, f2.get() ); }


Cuando tienes dos niveles de async, necesitas usar una promesa. P.ej:

void fun() { std::promise<int> p; std::future<int> f = p.get_future(); std::future<void> f2; auto f3 = std::async([&]{ // Do some other computation f2 = std::async([&]{ p.set_value(42);}); // Do some other work }); // Do some other work // Now wait for the result of async work; std::cout << f.get(); // Do some other work again // Wait for async threads to complete f3.wait(); f2.wait(); }


Usted no elige usar una promise lugar de las demás, usa una promise para cumplir un future junto con los demás. El ejemplo de código en cppreference.com da un ejemplo del uso de los cuatro:

#include <iostream> #include <future> #include <thread> int main() { // future from a packaged_task std::packaged_task<int()> task([](){ return 7; }); // wrap the function std::future<int> f1 = task.get_future(); // get a future std::thread(std::move(task)).detach(); // launch on a thread // future from an async() std::future<int> f2 = std::async(std::launch::async, [](){ return 8; }); // future from a promise std::promise<int> p; std::future<int> f3 = p.get_future(); std::thread( [](std::promise<int>& p){ p.set_value(9); }, std::ref(p) ).detach(); std::cout << "Waiting..."; f1.wait(); f2.wait(); f3.wait(); std::cout << "Done!/nResults are: " << f1.get() << '' '' << f2.get() << '' '' << f3.get() << ''/n''; }

huellas dactilares

Esperando ... ¡Hecho!

Los resultados son: 7 8 9

Los futuros se utilizan con los tres hilos para obtener sus resultados, y una promise se utiliza con el tercero para cumplir un future por otros medios que no sean un valor de retorno. Además, un solo hilo puede cumplir múltiples future s con diferentes valores a través de promise , que no puede hacer de otra manera.

Una manera fácil de pensar es que puede establecer un future devolviendo un valor o haciendo una promise . future no tiene un método set ; Esa funcionalidad es provista por promise . Usted elige lo que necesita en función de lo que permite la situación.