¿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::asyncEn 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::promiseEn 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:

wait () bloqueará un futuro dado hasta que se produzca el resultadoSi 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() );
}

Respuestas a la pregunta(2)

Su respuesta a la pregunta