web-dev-qa-db-fra.com

En attente de plusieurs futurs?

Je voudrais exécuter des tâches (threads de travail) du même type, mais pas plus d'un certain nombre de tâches à la fois. Lorsqu'une tâche se termine, son résultat est une entrée pour une nouvelle tâche qui peut alors être démarrée.

Existe-t-il un bon moyen de l'implémenter avec le paradigme async/future en C++ 11?

À première vue, il semble simple, vous lancez simplement plusieurs tâches avec:

std::future<T> result = std::async(...);

puis, exécutez result.get() pour obtenir le résultat asynchrone d'une tâche.

Cependant, le problème ici est que les futurs objets doivent être stockés dans une sorte de file d'attente et attendus un par un. Il est cependant possible de répéter encore et encore les futurs objets en vérifiant si certains d'entre eux sont prêts, mais ce n'est pas souhaité en raison d'une charge CPU inutile.

Est-il possible d'attendre any futur d'un ensemble donné pour être prêt et obtenir son résultat?

La seule option à laquelle je peux penser jusqu'à présent est une approche old-school sans async/futur. Plus précisément, la génération de plusieurs threads de travail et à la fin de chaque thread Poussez son résultat dans une file d'attente protégée par mutex notifiant le thread en attente via une variable de condition que la file d'attente a été mise à jour avec plus de résultats.

Existe-t-il une autre meilleure solution avec async/future possible?

45
alveko

La prise en charge des threads en C++ 11 n'était qu'une première passe, et bien que std::future Fonctionne, il ne prend pas encore en charge l'attente multiple.

Cependant, vous pouvez le simuler de manière relativement inefficace. Vous finissez par créer un fil d’assistance pour chaque std::future (Aïe, très cher), puis vous rassemblez leur "this future is ready" dans une file d’attente de messages synchronisée de plusieurs producteurs à consommateur unique, puis définissez une tâche consommateur qui distribue le fait qu'un std::future donné est prêt.

Le std::future Dans ce système n'ajoute pas beaucoup de fonctionnalités, et avoir des tâches qui déclarent directement qu'elles sont prêtes et colle leur résultat dans la file d'attente ci-dessus serait plus efficace. Si vous suivez cette route, vous pouvez écrire un wrapper qui correspond au modèle de std::async Ou std::thread, Et renvoyer un objet similaire à std::future Qui représente un message de file d'attente. Cela implique essentiellement de réimplémenter un morceau de la bibliothèque de concurrence.

Si vous voulez rester avec std::future, Vous pouvez créer shared_future S, et faire en sorte que chaque tâche dépendante dépende de l'ensemble de shared_future S: c'est-à-dire, faites-le sans planificateur central . Cela ne permet pas des choses comme les messages d'abandon/d'arrêt, que je considère comme essentiels pour un système de tâches multithread robuste.

Enfin, vous pouvez attendre C++ 2x , ou chaque fois que le TS simultané est replié dans la norme, pour résoudre le problème pour vous.

18

Vous pouvez créer tous les futurs de "génération 1" et donner tous ces futurs à vos tâches génération 2, qui attendront ensuite leur entrée elles-mêmes.

6
Sebastian Mach

Étant donné que le titre "Wating for multiple futures" attire des gens avec des questions comme "y a-t-il une attente pour une liste de futurs?". Vous pouvez le faire de manière adéquate en gardant une trace des threads en attente:

unsigned pending = 0;
for (size_t i = 0; i < N; ++i) {
    ++pending;
    auto callPause =
        [&pending, i, &each, &done]()->unsigned {
            unsigned ret = each();
            results[i] = ret;
            if (!--pending)
                // called in whatever thread happens to finish last
                done(results);
            return ret;
        };
    futures[i] = std::async(std::launch::async, each);
}

exemple complet

Il pourrait être possible d'utiliser std :: experimental :: when_all avec un opérateur d'étalement

0
ericP