Guía de concurrencia de C ++ <futuro> (2) std :: packaged_task

std :: packaged_task envuelve un objeto invocable y permite el acceso asincrónico a los resultados producidos por el objeto invocable. En el sentido de empaquetar el objeto invocable, std :: packaged_task es similar a std :: function, excepto que std :: packaged_task El resultado de ejecución del objeto invocable envuelto se pasa a un objeto std :: future (el objeto normalmente obtiene el resultado de la ejecución de la tarea std :: packaged_task en otro hilo).

El objeto std :: packaged_task contiene los dos elementos más básicos:

  1. La tarea empaquetada (tarea almacenada), la tarea (tarea) es un objeto invocable, como un puntero de función, un puntero de función miembro o un objeto de función
  2. Estado compartido (estado compartido), utilizado para guardar el valor de retorno de la tarea, puede usar el objeto std :: future para lograr el efecto de acceder de forma asincrónica al estado compartido

El objeto std :: future asociado con el estado compartido se puede obtener a través de std :: packged_task :: get_future. Después de llamar a esta función, los dos objetos comparten el mismo estado compartido, que se explica a continuación:

  • El objeto std :: packaged_task es un proveedor asincrónico, que establece el valor del estado compartido llamando a la tarea empaquetada en un momento determinado.
  • El objeto std :: future es un objeto de retorno asincrónico, a través del cual se puede obtener el valor del estado compartido, por supuesto, es necesario esperar a que la bandera de estado compartido esté lista cuando sea necesario.

El ciclo de vida del estado compartido de std :: packaged_task continúa hasta que se libera o destruye el último objeto asociado con él. El siguiente pequeño ejemplo habla aproximadamente sobre el uso de std :: packaged_task:

#include <iostream>     // std::cout
#include <future>       // std::packaged_task, std::future
#include <chrono>       // std::chrono::seconds
#include <thread>       // std::thread, std::this_thread::sleep_for

// count down taking a second for each value:
int countdown (int from, int to) {
    
    
    for (int i=from; i!=to; --i) {
    
    
        std::cout << i << '\n';
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    
    std::cout << "Finished!\n";
    return from - to;
}

int main ()
{
    
    
    std::packaged_task<int(int,int)> task(countdown); // 设置 packaged_task
    std::future<int> ret = task.get_future();         // 获得与 packaged_task 共享状态相关联的 future 对象.

    std::thread th(std::move(task), 10, 0);           // 创建一个新线程完成计数任务.

    int value = ret.get();                            // 等待任务完成并获取结果.

    std::cout << "The countdown lasted for " << value << " seconds.\n";

    th.join();
    
    return 0;
}

Nota: El valor obtenido por ret.get () es el valor de retorno de la función packaged_task vinculada a la tarea

std :: constructor packaged_task

defecto packaged_task () noexcept;
inicialización plantilla <clase Fn> explícita packaged_task (Fn && fn);
con asignador plantilla <clase Fn, clase Alloc> explícita packaged_task (allocator_arg_t aa, const Alloc & alloc, Fn && fn);
copiar [eliminado] packaged_task (const packaged_task &) = eliminar;
Muevete packaged_task (packaged_task && x) noexcept;

Hay 5 formas de constructor std :: packaged_task, pero la construcción de copias ha sido deshabilitada. A continuación se presenta brevemente la semántica de los constructores mencionados anteriormente:

  1. El constructor predeterminado inicializa un estado compartido vacío y el objeto packaged_task no tiene tareas empaquetadas.
  2. Inicialice un estado compartido y la tarea empaquetada se especifica mediante el parámetro fn.
  3. El constructor con un asignador de memoria personalizado es similar al constructor predeterminado, pero usa un asignador personalizado para asignar el estado compartido.
  4. El constructor de copias está deshabilitado.
  5. Mover constructor.

Los siguientes ejemplos presentan el uso de varios constructores:

#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::thread

int main ()
{
    
    
    std::packaged_task<int(int)> foo; // 默认构造函数.

    // 使用 lambda 表达式初始化一个 packaged_task 对象.
    std::packaged_task<int(int)> bar([](int x){
    
    return x*2;});

    foo = std::move(bar); // move-赋值操作,也是 C++11 中的新特性.

    // 获取与 packaged_task 共享状态相关联的 future 对象.
    std::future<int> ret = foo.get_future();

    std::thread(std::move(foo), 10).detach(); // 产生线程,调用被包装的任务.

    int value = ret.get(); // 等待任务完成并获取结果.
    std::cout << "The double of 10 is " << value << ".\n";

    return 0;
}

Similar a std :: promise, std :: packaged_task también deshabilita las operaciones de asignación ordinarias y solo permite operaciones de asignación de movimiento.

std :: packaged_task :: válido 介绍

Compruebe si el packaged_task actual está asociado con un estado compartido válido. Para el objeto packaged_task generado por el constructor predeterminado, la función devuelve falso, a menos que se realice una operación de asignación de movimiento o una operación de intercambio en el medio.

Considere el siguiente ejemplo:

#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::thread

// 在新线程中启动一个 int(int) packaged_task.
std::future<int> launcher(std::packaged_task<int(int)>& tsk, int arg)
{
    
    
    if (tsk.valid()) {
    
    
        std::future<int> ret = tsk.get_future();
        std::thread (std::move(tsk),arg).detach();
        return ret;
    }
    else 
        return std::future<int>();
}

int main ()
{
    
    
    std::packaged_task<int(int)> tsk([](int x){
    
    return x*2;});

    std::future<int> fut = launcher(tsk,25);

    std::cout << "The double of 25 is " << fut.get() << ".\n";

    return 0;
}

std :: packaged_task :: get_future 介绍

Devuelve un objeto futuro relacionado con el estado compartido del objeto packaged_task. El objeto futuro devuelto puede obtener un determinado valor o una excepción establecida por otro hilo en el estado compartido del objeto packaged_task.

Por favor, vea el ejemplo:

#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::thread

int main ()
{
    
    
    std::packaged_task<int(int)> tsk([](int x) {
    
     return x * 3; }); // package task

    std::future<int> fut = tsk.get_future();     // 获取 future 对象.

    std::thread(std::move(tsk), 100).detach();   // 生成新线程并调用packaged_task.

    int value = fut.get();                       // 等待任务完成, 并获取结果.

    std::cout << "The triple of 100 is " << value << ".\n";

    return 0;
}

std :: packaged_task :: operator () (Args… args) 介绍

Llame al objeto empaquetado por el objeto packaged_task (generalmente un puntero de función, objeto de función, expresión lambda, etc.), y el parámetro pasado es args. Usualmente suceden dos cosas al llamar a esta función:

  1. Si el objeto empaquetado por packaged_task se llama con éxito, el valor de retorno (si el objeto empaquetado tiene un valor de retorno) se guarda en el estado compartido de packaged_task.
  2. Si el objeto empaquetado por packaged_task no se puede llamar y se lanza una excepción, la excepción también se guardará en el estado compartido de packaged_task.

En los dos casos anteriores, el indicador del estado compartido se vuelve listo, por lo que otros subprocesos que esperan el estado compartido pueden obtener el valor del estado compartido o una excepción y continuar la ejecución.

El valor del estado compartido se puede obtener llamando a get en el objeto futuro (obtenido por get_future).

Dado que la tarea empaquetada se especifica cuando se construye packaged_task, el efecto de llamar al operator () está determinado por el objeto invocable especificado cuando se construye el objeto packaged_task:

  • Si la tarea empaquetada es un puntero de función o un objeto de función, llamar a std :: packaged_task :: operator () simplemente pasa los parámetros al objeto empaquetado.
  • Si la tarea empaquetada es un puntero a una función miembro no estática de la clase, entonces el primer parámetro de std :: packaged_task :: operator () debe especificarse como el objeto en el que se llama a la función miembro y los parámetros restantes se utilizan como parámetro de la función miembro.
  • Si la tarea empaquetada es una variable miembro no estática que apunta a la clase, entonces std :: packaged_task :: operator () solo permite un único parámetro.

std :: packaged_task :: make_ready_at_thread_exit 介绍

Esta función llamará a la tarea empaquetada y pasará parámetros a la tarea, similar a la función de miembro operator () de std :: packaged_task. Pero a diferencia de la función operator (), make_ready_at_thread_exit no establece inmediatamente el indicador de estado compartido en listo, sino que establece el indicador de estado compartido cuando el hilo sale.

Si el objeto futuro asociado con el estado compartido packaged_task está esperando en future :: get, la llamada actual future :: get se bloqueará hasta que el hilo salga. Una vez que el hilo sale, la llamada future :: get continúa ejecutándose o se lanza una excepción.

Tenga en cuenta que esta función ya ha establecido el valor del estado compartido de la promesa. Si hay otras operaciones para establecer o modificar el valor del estado compartido antes de que finalice el hilo, se lanzará future_error (promise_already_satisfied).

std :: packaged_task :: reset () introducción

Restablece el estado compartido de packaged_task, pero conserva la tarea empaquetada anterior.
Consulte el ejemplo, en este ejemplo, packaged_task se reutiliza muchas veces:

#include <iostream>
#include <cmath>
#include <thread>
#include <future>
 
int main()
{
    
    
    std::packaged_task<int(int,int)> task([](int a, int b) {
    
    
        return std::pow(a, b);
    });
    std::future<int> result = task.get_future();
    task(2, 9);
    std::cout << "2^9 = " << result.get() << '\n';
 
    task.reset();
    result = task.get_future();
    std::thread task_td(std::move(task), 2, 10);
    task_td.join();
    std::cout << "2^10 = " << result.get() << '\n';
}

std :: packaged_task :: swap () 介绍

Intercambie el estado compartido de packaged_task.

Supongo que te gusta

Origin blog.csdn.net/qq_24649627/article/details/114141454
Recomendado
Clasificación