web-dev-qa-db-fra.com

Pourquoi devrais-je utiliser std :: async?

J'essaie d'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, au moins sous linux avec gcc 4.8.1:

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

à ce stade, il est naturel que je me demande pourquoi choisir std :: async plutôt qu'un simple ensemble de foncteurs? C'est une solution qui ne s'adapte même pas du tout. Plus vous appelez d'avenir, moins votre programme sera réactif.

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

54
user2485710

Si vous avez besoin du résultat d’une opération asynchrone, alors vous devez bloquer, quelle que soit la bibliothèque que vous utilisez. L'idée est que vous devez choisir le moment où vous souhaitez bloquer et, espérons-le, ce faisant, vous bloquez pour une durée négligeable, car tout le travail a déjà été effectué. 

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

53
juanchopanza
  • ça s'appelle async, mais ça a vraiment un "comportement séquentiel",

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

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

Il ne bloque que si foo n’est pas terminé, mais s’il a été exécuté de manière asynchrone (par exemple, parce que vous utilisez la stratégie std::launch::async), il s’est peut-être terminé avant que vous en ayez besoin.

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

Faux, il n'est pas nécessaire de l'implémenter avec Pthreads (et sous Windows, ce n'est pas le cas, il utilise les fonctionnalités ConcRT.)

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

Parce qu'il garantit la sécurité des threads et propage les exceptions entre les threads. Pouvez-vous le faire avec un simple ensemble de foncteurs?

C'est une solution qui ne s'adapte même pas du tout. Plus vous appelez d'avenir, moins votre programme sera réactif.

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

Il est vrai qu'avec l'implémentation de GCC, si vous ne fournissez pas de stratégie de lancement, les versions actuelles ne seront jamais exécutées dans un nouveau fil (il y a un rapport de bugzilla ), mais c'est une propriété de cette implémentation, pas de std::async en général. Vous ne devez pas confondre 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 de se familiariser avec C++ 11.

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

Cela ne devrait pas bloquer:

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

En spécifiant la stratégie de lancement, vous forcez l'exécution asynchrone et si vous effectuez un autre travail pendant son exécution, le résultat sera prêt lorsque vous en aurez besoin.

74
Jonathan Wakely

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

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

Il existe de nombreuses façons de savoir que le résultat est déjà prêt. Vous pouvez interroger la future et lui demander (relativement simple), vous pouvez utiliser des verrous ou des données atomiques pour relayer le fait qu'elle est prête, vous pouvez créer un cadre pour livrer des éléments "finis" future dans une file d'attente avec laquelle les consommateurs peuvent interagir , vous pouvez utiliser des signaux de quelque sorte (bloquant plusieurs choses à la fois, ou interrogation).

Vous pouvez également terminer tout le travail que vous pouvez effectuer localement, puis bloquer le travail à distance.

Par exemple, imaginons un tri par 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. Une fois le tri effectué, le thread d'origine ne peut pas progresser tant que la deuxième tâche n'est pas terminée. Donc, il fait un .get() et bloque. Une fois que les deux moitiés ont été triées, il est alors possible de procéder à 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 vis-à-vis de ceux qui interagissent avec elle de l'extérieur. Lorsqu'elle est terminée, le tableau est trié.

Nous pouvons ensuite envelopper ceci dans une tâche std::async et avoir un tableau trié future. Si nous le souhaitons, nous pourrions ajouter une procédure de signature pour nous informer que la variable future est terminée, mais cela n'a de sens que si nous avons un fil en attente sur les signaux.

12

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

Si l'indicateur async est défini (c'est-à-dire policy & std :: launch :: async! = 0), alors async exécute la fonction f sur un thread d'exécution distinct comme si spawned by std :: thread (f, arguments ...), sauf que if la fonction f renvoie une valeur ou lève une exception, elle est stockée dans le fichier partagé état accessible via le std :: future que async retourne au votre interlocuteur.

C'est une propriété de Nice pour garder une trace des exceptions levées.

3
fatihk

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

il existe trois types de politique,

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

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

0
Masoud Bolhassani