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:
- std :: async con / std :: future (std :: shared_future) (cuando se desea ejecutar en un async de subproceso productor desechable de una sola vez)
- std :: packaged_task (cuando desea asignar un productor, pero aplazar la llamada al hilo)
- 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:
-
std::async
través de una devolución de llamada asíncrona, que devolverá una instanciastd::future
. -
std::packaged_task
, que, cuando se pasa a un hilo, invocará su devolución de llamada, actualizando así la instanciastd::future
asociada con esestd::packaged_task
. Este mecanismo permite la vinculación temprana de un productor, pero una invocación posterior. -
std::promise
, que le permite modificar sustd::future
asociado a través de su llamadaset_value()
. Con este control directo sobre la mutación de unstd::future
, debemos asegurarnos de que el diseño sea seguro para subprocesos si hay varios productores (usestd::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
:
- wait () bloqueará un futuro dado hasta que se produzca el resultado
- 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.