Guide de concurrence C ++ <future> (2) std :: packaged_task

std :: packaged_task encapsule un objet appelable et permet un accès asynchrone aux résultats produits par l'objet appelable. Dans le sens de l'empaquetage de l'objet appelable, std :: packaged_task est similaire à std :: function, sauf que std :: packaged_task le résultat de l'exécution de l'objet appelable encapsulé est passé à un objet std :: future (l'objet obtient généralement le résultat de l'exécution de la tâche std :: packaged_task dans un autre thread).

L'objet std :: packaged_task contient deux éléments les plus élémentaires:

  1. La tâche empaquetée (tâche stockée), la tâche (tâche) est un objet appelable, tel qu'un pointeur de fonction, un pointeur de fonction membre ou un objet de fonction
  2. État partagé (état partagé), utilisé pour enregistrer la valeur de retour de la tâche, vous pouvez utiliser l'objet std :: future pour obtenir l'effet d'accéder de manière asynchrone à l'état partagé

L'objet std :: future associé à l'état partagé peut être obtenu via std :: packged_task :: get_future. Après avoir appelé cette fonction, les deux objets partagent le même état partagé, qui est expliqué comme suit:

  • L'objet std :: packaged_task est un fournisseur asynchrone, qui définit la valeur de l'état partagé en appelant la tâche empaquetée à un certain moment.
  • L'objet std :: future est un objet de retour asynchrone, à travers lequel la valeur de l'état partagé peut être obtenue. Bien sûr, il est nécessaire d'attendre que l'indicateur d'état partagé soit prêt si nécessaire.

Le cycle de vie de l'état partagé de std :: packaged_task continue jusqu'à ce que le dernier objet qui lui est associé soit libéré ou détruit. Le petit exemple suivant décrit à peu près l'utilisation 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;
}

Remarque: La valeur obtenue par ret.get () est la valeur de retour de la fonction packaged_task liée à la tâche

constructeur std :: packaged_task

défaut packaged_task () noexcept;
initialisation template <classe Fn> explicit packaged_task (Fn && fn);
avec allocateur template <classe Fn, classe Alloc> explicit packaged_task (allocator_arg_t aa, const Alloc & alloc, Fn && fn);
copie [supprimé] packaged_task (const packaged_task &) = supprimer;
bouge toi packaged_task (packaged_task && x) noexcept;

Il existe 5 formes de constructeur std :: packaged_task, mais la construction de la copie a été désactivée. Ce qui suit présente brièvement la sémantique des constructeurs mentionnés ci-dessus:

  1. Le constructeur par défaut initialise un état partagé vide et l'objet packaged_task n'a pas de tâches empaquetées.
  2. Initialisez un état partagé et la tâche empaquetée est spécifiée par le paramètre fn.
  3. Le constructeur avec un allocateur de mémoire personnalisé est similaire au constructeur par défaut, mais utilise un allocateur personnalisé pour allouer l'état partagé.
  4. Le constructeur de copie est désactivé.
  5. Déplacer le constructeur.

Les exemples suivants présentent l'utilisation de divers constructeurs:

#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;
}

Semblable à std :: promise, std :: packaged_task désactive également les opérations d'assignation ordinaires et n'autorise que les opérations d'assignation de déplacement.

std :: packaged_task :: valide 介绍

Vérifiez si la fonction packaged_task actuelle est associée à un état partagé valide. Pour l'objet packaged_task généré par le constructeur par défaut, la fonction renvoie false, sauf si une opération d'attribution de déplacement ou de permutation est effectuée au milieu.

Prenons l'exemple suivant:

#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 介绍

Renvoie un futur objet lié à l'état partagé de l'objet packaged_task. L'objet futur retourné peut obtenir une certaine valeur ou une exception définie par un autre thread sur l'état partagé de l'objet packaged_task.

S'il vous plaît voir l'exemple:

#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) 介绍

Appelez l'objet empaqueté par l'objet packaged_task (généralement un pointeur de fonction, un objet fonction, une expression lambda, etc.) et le paramètre transmis est args. Deux choses se produisent généralement lors de l'appel de cette fonction:

  1. Si l'objet empaqueté par packaged_task est appelé avec succès, la valeur de retour (si l'objet empaqueté a une valeur de retour) est enregistrée dans l'état partagé de packaged_task.
  2. Si l'objet empaqueté par packaged_task échoue à être appelé et qu'une exception est levée, l'exception sera également enregistrée dans l'état partagé de packaged_task.

Dans les deux cas ci-dessus, l'indicateur de l'état partagé devient prêt, de sorte que les autres threads en attente de l'état partagé peuvent obtenir la valeur de l'état partagé ou une exception et continuer l'exécution.

La valeur de l'état partagé peut être obtenue en appelant get sur l'objet futur (obtenu par get_future).

Étant donné que la tâche packagée est spécifiée lors de la construction de packaged_task, l'effet de l'appel de operator () est déterminé par l'objet appelable spécifié lors de la construction de l'objet packaged_task:

  • Si la tâche packagée est un pointeur de fonction ou un objet fonction, l'appel de std :: packaged_task :: operator () transmet simplement les paramètres à l'objet packagé.
  • Si la tâche empaquetée est un pointeur vers une fonction membre non statique de la classe, alors le premier paramètre de std :: packaged_task :: operator () doit être spécifié comme l'objet sur lequel la fonction membre est appelée, et les paramètres restants sont utilisés comme paramètre de la fonction membre.
  • Si la tâche empaquetée est une variable membre non statique qui pointe vers la classe, alors std :: packaged_task :: operator () n'autorise qu'un seul paramètre.

std :: packaged_task :: make_ready_at_thread_exit 介绍

Cette fonction appellera la tâche packagée et passera des paramètres à la tâche, similaire à la fonction membre operator () de std :: packaged_task. Mais contrairement à la fonction operator (), make_ready_at_thread_exit ne définit pas immédiatement l'indicateur d'état partagé sur prêt, mais définit l'indicateur d'état partagé lorsque le thread se termine.

Si le futur objet associé à l'état partagé packaged_task attend à future :: get, l'appel de future :: get actuel sera bloqué jusqu'à ce que le thread se termine. Une fois le thread terminé, l'appel future :: get continue de s'exécuter ou une exception est levée.

Notez que cette fonction a déjà défini la valeur de l'état partagé de la promesse. S'il existe d'autres opérations pour définir ou modifier la valeur de l'état partagé avant la fin du thread, future_error (promise_already_satisfied) sera renvoyée.

std :: packaged_task :: reset () introduction

Réinitialisez l'état partagé de packaged_task, mais conservez la tâche packagée précédente.
Veuillez consulter l'exemple, dans cet exemple, packaged_task est réutilisé plusieurs fois:

#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 () 介绍

Échangez l'état partagé de packaged_task.

Je suppose que tu aimes

Origine blog.csdn.net/qq_24649627/article/details/114141454
conseillé
Classement