2010-03-30 3 views
36

Quand choisira-t-on d'utiliser Rx sur TPL ou les 2 frameworks sont-ils orthogonaux? D'après ce que je comprends, Rx est principalement destiné à fournir une abstraction sur les événements et permettre la composition, mais il permet également de fournir une abstraction sur les opérations asynchrones. en utilisant les surcharges Createxx et les surcharges et annulation Fromxxx en disposant l'IDisposable retourné. TPL fournit également une abstraction pour les opérations via les capacités de tâche et d'annulation.TPL vs Reactive Framework

Mon dilemme est quand utiliser quoi et dans quels scénarios?

+0

lien vers une question plus ancienne, fermée, en double: http://stackoverflow.com/questions/2138361/reactive-framework-vs-plinq-vs-task-parallel-library-vs-parallel-extensions/2188259 – yzorg

Répondre

39

Le but principal de Rx n'est pas de fournir une abstraction sur les événements. Ce n'est que l'un de ses résultats. Son objectif principal est de fournir un modèle push composable pour les collections.

Le cadre réactif (Rx) est basé sur IObservable<T> étant le dual mathématique de IEnumerable<T>. Donc, plutôt que de "tirer" des éléments d'une collection en utilisant IEnumerable<T>, nous pouvons avoir des objets "poussés" à nous via IObservable<T>.

Bien sûr, lorsque nous recherchons des sources observables, des événements comme les événements & sont d'excellents candidats.

Le cadre réactif nécessite naturellement un modèle multi-thread pour pouvoir observer les sources de données observables et gérer les requêtes et les abonnements. Rx fait vraiment un usage intensif de la TPL pour ce faire. Donc, si vous utilisez Rx, vous utilisez implicitement le TPL.

Vous utiliserez directement le TPL si vous souhaitez un contrôle direct sur vos tâches.

Mais si vous avez des sources de données que vous souhaitez observer et effectuer des requêtes contre alors je recommande fortement le cadre réactif.

+2

Je voudrais Par exemple, si vous omettez le threading, les expressions "fournir une abstraction sur les événements" équivaut à "pousser le modèle pour les collections d'objets d'événements (également appelés objets DTO)". – yzorg

+3

Très bonne réponse, et dans les applications pratiques est largement vrai. Cependant Rx peut être utilisé sans le TPL du tout. En outre, Rx peut être utilisé dans une application à un seul thread sans aucune concurrence (un peu comme WPF ou NodeJS réalisent l'asynchronisme dans un seul thread). J'apprécie énormément votre pitch d'ascenseur pour le dernier mot, je dirais "Son but principal est de fournir un modèle composable push pour les séquences de données". J'ai trouvé que plus tôt les devs pensent en séquences au lieu de collections, plus vite ils font groover Rx. –

23

Quelques directives J'aime suivre:

  • Suis-je traiter les données que je ne proviennent pas. Des données qui arrivent quand cela vous plaît? Puis RX.
  • Est-ce que je crée des calculs et dois gérer la concurrence? Puis TPL. Est-ce que je gère plusieurs résultats et que je dois en choisir un en fonction du temps? Puis RX.
11

J'aime les points de balle de Scott W. Pour mettre quelques exemples plus concrets dans cartes Rx vraiment bien à

  • consommatrice flux
  • effectuer des travaux async non bloquante comme les requêtes Web.
  • événements de diffusion (soit.événements net comme mouvement de souris ou service Bus événements de type de message)
  • Composer "flux" d'événements ensemble
  • opérations de style Linq
  • L'exposition des flux de données à partir de votre API publique

TPL semble carte bien à

  • parallélisation interne du travail
  • effectuer un travail asynchrone non-bloquant comme demande web
  • effectuant des flux de travail et continuations

Une chose que j'ai remarqués avec IObservable (Rx) est qu'il devient omniprésente. Une fois dans votre base de code, comme il sera sans doute exposé via d'autres interfaces, il apparaîtra dans toute votre application. J'imagine que cela peut être effrayant au début, mais la plupart de l'équipe est assez à l'aise avec Rx maintenant et aime la quantité de travail qu'il nous permet d'économiser.

IMHO Rx sera la bibliothèque dominante sur le TPL car elle est déjà supportée par .NET 3.5, 4.0, Silverlight 3, Silverlight 4 et Javascript. Cela signifie que vous devez effectivement apprendre un style et qu'il est applicable à de nombreuses plateformes.

EDIT: J'ai changé d'avis à propos de Rx étant dominant sur TPL. Ils résolvent différents problèmes et ne devraient donc pas vraiment être comparés comme ça. Avec .NET 4.5/C# 5.0, les mots-clés async/await vont nous lier davantage au TPL (ce qui est bien). Pour une discuson profonde sur Rx vs événements vs TPL etc. consultez la first chapter de mon livre en ligne IntroToRx.com

9

mise à jour, Décembre 2016: Si vous avez 30 minutes, je vous recommande de lire le compte de première main de Joe Duffy au lieu de ma spéculation. Je pense que mon analyse résiste bien, mais si vous avez trouvé cette question, je vous recommande fortement de voir le blog au lieu de ces réponses car en plus de TPL vs Rx.NET, il couvre également des projets de recherche MS (Midori, Cosmos).

http://joeduffyblog.com/2016/11/30/15-years-of-concurrency/


Je pense que MS a fait une grosse erreur sur correction après .NET 2.0 est sorti. Ils ont introduit de nombreuses API de gestion d'accès simultanées différentes à la fois dans différentes parties de l'entreprise.

  • Steven Toub poussait dur pour les primitives thread-safe pour remplacer l'événement (qui a commencé comme Future<T> et transformé en Task<T>)
  • MS Research avait MIN-LINQ et Reactive Extensions (Rx)
  • Matériel/intégré avait robotics cuntime (CCR)

En attendant de nombreuses équipes de l'API gérées ont essayé de vivre avec APM et Threadpool.QueueUserWorkItem(), ne sachant pas si Toub allait gagner son combat pour expédier Future<T>/Task<T> dans mscorlib.dll. En fin de compte, il semble qu'ils ont couvert, et a expédié à la fois Task<T> et IObservable<T> dans mscorlib, mais n'a pas permis d'autres API Rx (pas même ISubject<T>) dans mscorlib.Je pense que cette haie a fini par causer énormément de duplication (plus tard) et gaspillé l'effort à l'intérieur et à l'extérieur de l'entreprise.

Pour voir duplication: Task vs IObservable<Unit>, Task<T> contre AsyncSubject<T>, Task.Run() contre Observable.Start(). Et ce n'est que la pointe de l'iceberg. Mais à un niveau plus élevé compte:

  • StreamInsight - flux d'événements SQL, code optimisé natif, mais les requêtes d'événements définis en utilisant la syntaxe LINQ
  • TPL Dataflow - construit sur TPL, construit en parallèle à Rx, optimisé pour tordant le parallélisme de filetage, pas bon pour composer des requêtes
  • Rx - expressivité étonnante, mais lourde de péril. Mélange les flux «chauds» avec les méthodes d'extension IEnumerable -style, ce qui signifie que vous bloquez très facilement pour toujours (l'appel First() sur un flux chaud ne revient jamais). Les limites d'ordonnancement (limitation du parallélisme) se font via des méthodes d'extension plutôt étranges, qui sont étrangement implicites et difficiles à obtenir. Si vous commencez à apprendre Rx réserver longtemps pour apprendre tous les pièges à éviter. Mais Rx est vraiment la seule option pour composer des flux d'événements complexes ou vous avez besoin d'un filtrage/interrogation complexe.

Je ne pense pas que Rx a une chance de se faire adopter à large échelle jusqu'à ce que MS expédie ISubject<T> dans mscorlib. Ce qui est triste, car Rx contient des types concrets (génériques) très utiles, comme TimeInterval<T> et Timestamped<T>, ce qui je pense devrait être dans Core/mscorlib comme Nullable<T>. En outre, System.Reactive.EventPattern<TEventArgs>.

+1

Orthographe hilare. –

+1

@DaveSexton IObservalbe? – yzorg

+1

Je pense que @DaveSexton signifiait SteamInsight. Mais sur une note sérieuse, merci pour l'analyse la plus insignifiante des cadres! Cette OMI aurait dû être la réponse acceptée! – kkm

0

J'ai fait une application Windows Phone. Commencé avec RX. Le problème était que, à un moment donné, j'ai utilisé une nouvelle version de RX et devinez quoi? Beaucoup de fonctions ont l'attribut "obsolète". Alors j'ai commencé avec TPL et maintenant j'ai un projet mixte avec les deux. Mon conseil est que si vous utilisez beaucoup d'appels Web asynchrones, il vaut mieux utiliser TPL. Comme déjà écrit RX utilise TPL alors pourquoi ne pas utiliser TPL depuis le début.

+0

L'un des principaux avantages de Rx est que async le code devient beaucoup plus facile à écrire et à suivre. Avec TPL, vous finirez par gérer des rappels imbriqués rendant le code difficile à suivre et à déboguer. L'équipe Rx aurait proposé des appels "équivalents" pour ceux marqués "obsolètes", avez-vous fait référence à MSDN pour les appels obsolètes pour trouver leurs nouveaux équivalents? –

+0

Je vais réécrire mon application en tant qu'application universelle et utiliser l'application de référence PRISM AdventureWorksShopper comme référence. Ce que je peux voir dans cette application est que l'architecture est très bonne et toutes les fonctionnalités RX sont disponibles en TPL, donc plus besoin de RX. Je ne pense pas que TPL est plus difficile à lire que RX pour moi, la courbe d'apprentissage est la même. Ce que je veux, c'est un cadre d'avenir. Je n'aime pas obsolète. Obsolète signifie: Nous n'avons plus l'argent pour le supporter. En tout cas c'est ce que je pense. –

4

Je dirais que TPL Dataflow couvre un sous-ensemble spécialisé de fonctionnalités dans Rx. Le flux de données est destiné au traitement de données qui peut prendre un temps mesurable, tandis que Rx concerne les événements, tels que la position de la souris, les états d'erreur, etc., où le temps de traitement est négligeable. Exemple: votre gestionnaire "subscribe" est asynchrone et vous ne voulez pas plus d'un exécuteur à la fois. Avec Rx vous devez bloquer, il n'y a pas d'autre moyen de contourner cela, parce que Rx est indépendant de l'async et ne menace pas async d'une manière spéciale dans de nombreux endroits.

.Subscribe(myAsyncHandler().Result) 

Si vous ne bloquez pas, alors Rx considérera que l'action est terminée tout gestionnaire est toujours en cours d'exécution en mode asynchrone.

Vous pourriez penser que si vous faites

.ObserveOn(Scheduler.EventLoopSchedule) 

que problème est résolu. Mais cela va casser votre flux de travail .Complete(), parce que Rx pensera que c'est fait dès qu'il planifie l'exécution et vous quitterez votre application sans attendre la fin de l'opération asynchrone.

Si vous ne voulez pas autoriser plus de 4 tâches asynchrones simultanées, Rx n'offre rien de prêt à l'emploi. Vous pouvez peut-être pirater quelque chose en implémentant votre propre planificateur, tampon, etc.

TPL Dataflow offre une très belle solution dans ActionBlock.Il peut limiter les actions simultanées à un certain nombre et il comprend les opérations asynchrones, ainsi l'appel de Complete() et l'attente de Completed feront exactement ce à quoi vous vous attendez: attendre que toutes les tâches asynchrones en cours se terminent.

Une autre caractéristique de TPL est la "contre-pression". Disons que vous avez découvert une erreur dans votre routine de traitement et devez recalculer les données du mois dernier. Si vous vous abonnez à votre source en utilisant Rx, et que votre pipeline contient des tampons illimités, ou ObserveOn, vous manquerez de mémoire en l'espace de quelques secondes car la source continuera à lire plus vite que le traitement ne peut en traiter. Même si vous implémentez le blocage du consommateur, votre source peut souffrir de bloquer les appels, par exemple si la source est asynchrone. En TPL vous pouvez mettre en œuvre comme source

while(...) 
    await actionBlock.SendAsync(msg) 

qui ne bloque encore la source ne sera pas attendre tout gestionnaire est surchargé. Dans l'ensemble, j'ai trouvé que Rx est bien adapté pour les actions qui sont le temps et la lumière de calcul. Si le temps de traitement devient important, vous êtes dans le monde des effets secondaires étranges et du débogage ésotérique.

Bonne nouvelle: les blocs TPL Dataflow jouent très bien avec Rx. Ils ont des adaptateurs AsObserver/AsObservable et vous pouvez les coller au milieu du pipeline Rx si nécessaire. Mais Rx a beaucoup plus de modèles et de cas d'utilisation. Donc, ma règle générale est de commencer avec Rx et d'ajouter TPL Dataflow si nécessaire.

Questions connexes