11 votes

Tasks vs. TPL Dataflow vs. Async/Await, lequel utiliser quand ?

J'ai lu un certain nombre de documents techniques, rédigés par l'équipe de Microsoft ou par d'autres auteurs, qui détaillent les fonctionnalités de la nouvelle bibliothèque TPL Dataflow, des cadres de concurrence async/await et de TPL. Cependant, je n'ai pas vraiment trouvé de document qui définisse clairement lequel utiliser et quand. Je suis conscient que chacun a sa place et son applicabilité, mais je m'interroge plus particulièrement sur la situation suivante :

J'ai un modèle de flux de données qui fonctionne entièrement en cours de processus. Au sommet se trouve un composant de génération de données (A) qui génère des données et les transmet à un composant de traitement (B) soit par des liens de blocs de flux de données, soit par des événements d'augmentation. Certaines parties de (B) doivent s'exécuter de manière synchrone, tandis que (A) bénéficie massivement du parallélisme, car la plupart des processus sont liés aux E/S ou au CPU (lecture de données binaires sur le disque, puis désérialisation et tri). Enfin, le composant de traitement (B) transmet les résultats transformés à (C) pour une utilisation ultérieure.

Je me demande spécifiquement quand utiliser les tâches, async/await et les blocs de flux de données TPL dans les cas suivants :

  • Lancement de la composante de génération de données (A). Il est clair que je ne veux pas verrouiller le gui/dashboard, donc ce processus devrait être exécuté sur un autre thread/tâche.

  • Comment appeler des méthodes dans (A), (B) et (C) qui ne sont pas directement impliquées dans le processus de génération et de traitement des données, mais qui effectuent un travail de configuration dont le retour peut prendre plusieurs centaines de millisecondes/secondes. J'ai l'impression que c'est là qu'async/await fait merveille ?

  • Ce qui me pose le plus de problèmes, c'est la manière de concevoir au mieux le passage des messages d'un composant à l'autre. TPL Dataflow semble très intéressant mais il est parfois trop lent pour mes besoins. (Note à la fin concernant les problèmes de performance). Si je n'utilise pas TPL Dataflow, comment puis-je obtenir la réactivité et la concurrence par le passage de données inter-tâches/concurrentes en cours de processus ? Par exemple, si je déclenche un événement dans une tâche, le gestionnaire d'événement souscrit s'exécute dans la même tâche au lieu d'être transmis à une autre tâche, n'est-ce pas ? En résumé, comment le composant (A) peut-il vaquer à ses occupations après avoir transmis des données au composant (B) pendant que ce dernier récupère les données et se concentre sur leur traitement ? Quel est le meilleur modèle de concurrence à utiliser ici ? J'ai implémenté des blocs de flux de données ici, mais est-ce vraiment la meilleure approche ?

  • Je pense que les points ci-dessus résument ma difficulté à concevoir et à mettre en œuvre des composants de type API en utilisant des pratiques standard ? Les méthodes doivent-elles être conçues de manière asynchrone, les entrées de données sous forme de blocs de flux de données et les sorties de données sous forme de blocs de flux de données ou d'événements ? Quelle est la meilleure approche en général ? Je pose la question parce que la plupart des composants mentionnés ci-dessus sont censés fonctionner indépendamment, de sorte qu'ils peuvent essentiellement être remplacés ou modifiés indépendamment en interne sans avoir à réécrire les accesseurs et la sortie.

Note sur la performance : J'ai mentionné que les blocs TPL Dataflow sont parfois lents. Je m'occupe d'un type d'application à haut débit et faible latence et je vise des limites d'E/S de disque et donc les blocs de flux de données TPL fonctionnent souvent beaucoup plus lentement que, par exemple, une unité de traitement synchrone. Le problème est que je ne sais pas comment intégrer le processus dans sa propre tâche ou modèle concurrent pour obtenir quelque chose de similaire à ce que les blocs tpl dataflow font déjà, mais sans la surcharge qui vient avec tpl df.

12voto

Stephen Cleary Points 91731

On dirait que vous avez un système "push". Tout simplement async Le code ne gère que les scénarios "pull".

Vous avez le choix entre TPL Dataflow et Rx . Je pense que TPL Dataflow est plus facile à apprendre, mais puisque vous l'avez déjà essayé et qu'il ne fonctionnera pas dans votre situation, j'essaierais Rx.

Rx aborde le problème sous un angle très différent : il est centré sur les "flux d'événements" plutôt que sur le "maillage d'acteurs" de TPL Dataflow. Les versions récentes de Rx sont très async -Vous pouvez donc utiliser async des délégués à plusieurs endroits de votre filière Rx.

En ce qui concerne la conception de votre API, TPL Dataflow et Rx fournissent tous deux des interfaces que vous devez implémenter : IReceivableSourceBlock / ITargetBlock pour TPL Dataflow, et IObservable / IObserver pour Rx. Vous pouvez simplement relier les implémentations aux points d'extrémité de votre maillage interne (TPL Dataflow) ou de votre requête (Rx). De cette façon, vos composants ne sont qu'un "bloc" ou un "observable/observateur/sujet" qui peut être composé dans d'autres "mailles" ou "requêtes".

Enfin, pour votre async il suffit d'utiliser le modèle d'usine. Votre implémentation peut appeler Task.Run pour effectuer la configuration sur un thread pool.

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