83 votes

Pourquoi devrais-je utiliser std::async ?

Je cherche à explorer toutes les options du nouveau standard C++11 en profondeur, tout en utilisant std::async et en lisant sa définition, j'ai remarqué 2 choses, du moins sous Linux avec gcc 4.8.1 :

  • Il est appelé async, mais il a un comportement vraiment "séquentiel", essentiellement dans la ligne où vous appelez le future associé à votre fonction foo, le programme se bloque jusqu'à ce que l'exécution de foo soit terminée.
  • Il dépend exactement de la même bibliothèque externe que d'autres solutions meilleures et non bloquantes, ce qui signifie pthread, si vous voulez utiliser std::async vous avez besoin de pthread.

À ce stade, il est naturel pour moi de me demander pourquoi choisir std::async plutôt qu'un simple ensemble de foncteurs ? C'est une solution qui ne scale même pas du tout, plus vous appelez de futures, moins réactif sera votre programme.

Est-ce que je manque quelque chose ? Pouvez-vous montrer un exemple qui est garanti d'être exécuté de manière asynchrone et non bloquante ?

91voto

Jonathan Wakely Points 45593
  • il est appelé asynchrone, mais il a un comportement vraiment "séquentiel",

Non, si vous utilisez la politique std::launch::async alors cela s'exécute de manière asynchrone dans un nouveau thread. Si vous ne spécifiez pas de politique, cela pourrait s'exécuter dans un nouveau thread.

essentiellement, dans la ligne où vous appelez le futur associé à votre fonction asynchrone foo, le programme se bloque jusqu'à ce que l'exécution de foo soit terminée.

Cela ne se bloque que si foo n'est pas terminé, mais s'il a été exécuté de manière asynchrone (par exemple, en utilisant la politique std::launch::async

`

  • cela dépend de la même bibliothèque externe que d'autres, et surtout, de meilleures solutions non bloquantes, ce qui signifie pthread, si vous voulez utiliser std::async vous avez besoin de pthread.

`

``

Faux, cela n'a pas besoin d'être implémenté en utilisant Pthreads (et sur Windows ce n'est pas le cas, cela utilise les fonctionnalités ConcRT.)

à ce stade, il est naturel pour moi de me demander pourquoi choisir std::async plutôt qu'un simple ensemble de foncteurs ?

Parce que cela garantit la sûreté de thread et propage les exceptions entre les threads. Pouvez-vous faire ça avec un simple ensemble de foncteurs ?

C'est une solution qui ne se met même pas à l'échelle du tout, plus vous appelez de futurs, moins votre programme sera réactif.

Pas nécessairement. Si vous ne spécifiez pas la politique de lancement alors une implémentation intelligente peut décider s'il faut démarrer un nouveau thread, ou renvoyer une fonction différée, ou renvoyer quelque chose qui décidera plus tard, lorsque plus de ressources seront disponibles.

Maintenant, il est vrai qu'avec l'implémentation de GCC, si vous ne fournissez pas de politique de lancement alors avec les versions actuelles cela ne s'exécutera jamais dans un nouveau thread (il y a un rapport bugzilla pour cela) mais c'est une propriété de cette implémentation, pas de std::async en général. Ne confondez pas la spécification dans la norme avec une implémentation particulière. Lire l'implémentation d'une bibliothèque standard est une mauvaise façon d'apprendre sur le C++11.

Pouvez-vous montrer un exemple qui est garanti d'être exécuté de manière asynchrone, non bloquante ?

Cela ne devrait pas se bloquer :

auto fut = std::async(std::launch::async, doSomethingThatTakesTenSeconds);
auto result1 = doSomethingThatTakesTwentySeconds();
auto result2 = fut.get();

En spécifiant la politique de lancement, vous forcez l'exécution asynchrone, et si vous faites d'autres travaux pendant son exécution alors le résultat sera prêt lorsque vous en aurez besoin.

``

73voto

juanchopanza Points 115680

Si vous avez besoin du résultat d'une opération asynchrone, alors vous devez bloquer, peu importe la bibliothèque que vous utilisez. L'idée est que vous choisissez quand bloquer, et, espérons-le, lorsque vous le faites, vous bloquez pendant un temps négligeable car tout le travail a déjà été fait.

Notez également que std::async peut être lancé avec les politiques std::launch::async ou std::launch::deferred. Si vous ne le spécifiez pas, l'implémentation est autorisée à choisir, et elle pourrait bien choisir d'utiliser une évaluation différée, ce qui entraînerait que tout le travail soit fait lorsque vous tentez d'obtenir le résultat de l'avenir, entraînant un blocage plus long. Donc si vous voulez vous assurer que le travail est fait de manière asynchrone, utilisez std::launch::async.

17voto

Yakk Points 31636

Je pense que votre problème concerne std::future en disant qu'il bloque sur get. Il ne bloque que si le résultat n'est pas déjà prêt.

Si vous parvenez à ce que le résultat soit déjà prêt, ce n'est pas un problème.

Il existe de nombreuses façons de savoir si le résultat est déjà prêt. Vous pouvez interroger le future et lui demander (relativement simple), vous pouvez utiliser des verrous ou des données atomiques pour indiquer le fait qu'il est prêt, vous pouvez mettre en place un cadre pour fournir des éléments future "terminés" dans une file d'attente avec laquelle les consommateurs peuvent interagir, vous pouvez utiliser des signaux de quelque sorte que ce soit (qui consiste simplement à bloquer sur plusieurs éléments en même temps, ou à interroger).

Ou, vous pouvez finir tout le travail que vous pouvez faire localement, puis bloquer sur le travail distant.

Par exemple, imaginez un tri fusion récursif parallèle. Il divise le tableau en deux morceaux, puis effectue un tri async sur un morceau tout en triant l'autre morceau. Une fois qu'il a terminé de trier sa moitié, le thread d'origine ne peut pas progresser tant que la deuxième tâche n'est pas terminée. Il fait donc un .get() et bloque. Une fois que les deux moitiés ont été triées, il peut alors effectuer une fusion (en théorie, la fusion peut également être effectuée au moins partiellement en parallèle).

Cette tâche se comporte comme une tâche linéaire pour ceux qui interagissent avec elle de l'extérieur - une fois terminée, le tableau est trié.

Nous pouvons alors envelopper cela dans une tâche std::async, et avoir un tableau trié future. Si nous le souhaitons, nous pourrions ajouter une procédure de signalisation pour nous informer que le future est terminé, mais cela n'a de sens que si nous avons un thread en attente des signaux.

3voto

FatihK Points 4776

Dans la référence: http://en.cppreference.com/w/cpp/thread/async

Si le drapeau async est activé (c'est-à-dire que la politique & std::launch::async != 0), alors async exécute la fonction f sur un thread d'exécution séparé comme si elle était lancée par std::thread(f, args...), à l'exception que si la fonction f renvoie une valeur ou lance une exception, celle-ci est stockée dans l'état partagé accessible via le std::future que async retourne à l'appelant.

C'est une bonne propriété de conserver un enregistrement des exceptions levées.

0voto

msoodb Points 13

http://www.cplusplus.com/reference/future/async/

Il existe trois types de politiques :

  1. std::launch::async
  2. std::launch::deferred
  3. std::launch::async|std::launch::deferred

par défaut, std::launch::async|std::launch::deferred est passé à std::async.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X