¿Cuándo es una buena idea usar std :: promise sobre los otros mecanismos std :: thread?
Estoy tratando de establecer algunas heurísticas para ayudarme a decidir el apropiadostd::thread
clase a utilizar.
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 comprensión decente decuándo usar Los dos primeros, pero todavía no estoy claro acerca destd::promise
.
std::future
en conjunción con unstd::async
call, transforma efectivamente un callback / functor / lambda en producción en una llamada asíncrona (que devuelve inmediatamente, por definición). Un consumidor singular puede llamarstd::future::get()
, una llamada de bloqueo, para recuperar sus resultados.
std::shared_future
Es solo una versión que permite múltiples consumidores.
Si quieres unir unstd::future
valor con un callback productor,pero desea diferir la invocación real a un momento posterior (cuando asocia la tarea a un subproceso de generación), std::packaged_task
Es la elección correcta. Pero ahora, desde el correspondientestd::future
alstd::package_task
podría, en el caso general, ser accedido por múltiples hilos, tendremos que tener cuidado de usar unstd::mutex
. Tenga en cuenta que constd::async
En el primer caso, no tenemos que preocuparnos por el bloqueo.
Habiendo leídoalgunos enlaces interesantes en la 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 destd::thread
.
En otras palabras, sería bueno tener un ejemplo útil de lo que puedo hacer con unstd::promise
eseno poder Se hará con los otros mecanismos.
RESPONDER
A std::future
es una bestia extraña: en general, no puedes modificar su valor directamente.
Tres productores que pueden modificar su valor son:
std::async
a través de una devolución de llamada asíncrona, que devolverá unstd::future
ejemplo.std::packaged_task
, que, cuando se pasa a un hilo, invocará su devolución de llamada actualizando asístd::future
instancia asociada con esostd::packaged_task
. Este mecanismo permite la vinculación temprana de un productor, pero una invocación posterior.std::promise
, lo que permite modificar sus asociadosstd::future
a través de suset_value()
llamada. 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 (usostd::mutex
como sea necesario).Yo creo queLa 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 hay que tener en cuenta que unastd::mutex
puede ser necesario, ya que la promesa puede ser accesible desde diferentes hilos, dependiendo del uso.
También,La respuesta de David Rodríguez 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 unastd::mutex
en un contenedor de resultados, y un hilo o un grupo de productores para actuar en el contenedor? ¿Qué hace usandostd::promise
En su lugar, ¿me compran, además de una legibilidad extra frente a un contenedor de resultados?
El control parece ser mejor en elstd::promise
versión:
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() );
}