web-dev-qa-db-fra.com

Quelle est la différence entre les structures Push et Pull telles que IEnumerable <T> et IObservable <T>

Dans chaque conversation technique ou dans chaque article de blog que j'ai lu sur IEnumerable et IObservable j'ai lu cela, IEnumerable est une structure basée sur l'extraction et IObservable est une structure basée sur Push. 

J'ai lu cela avecIObservable/ nous avons des appels asynchrones, où rien n'est bloqué et où tout est basé sur Push.

Mais, mais, mais ...

Qu'est-ce que cela signifie vraiment?Pushbased etpullbased

Parce que, à mon avis, dans IEnumerable , nous pouvons également insérer des données dans la structure et en extraire des données, j'ai vraiment perdu en termes techniques et en idées.

S'il vous plaît, expliquez-moi normalement et humainement la différence entre ces deux structures et la différence entre les structures basées sur Push et les structures basées sur Pull.

Merci.

16
Tornike Gomareli

S'il vous plaît, d'une manière normale et humaine, expliquez-moi la différence

OK, faisons des toasts. C'est la chose humaine la plus normale que je fasse.

Pull basé:

// I want some toast.  I will pull it out of the toaster when it is done.
// I will do nothing until the toast is available.
Toast t = toaster.MakeToast();
t.AddJam();
// Yum.

Push basé:

// I want some toast.  But it might take a while and I can do more work
// while I am waiting:
Task<Toast> task = toaster.MakeToastAsync();
Toast t = await task;
// await returns to the caller if the toast is not ready, and assigns
// a callback. When the toast is ready, the callback causes this method
// to start again from this point:
t.AddJam();
// Yum.

Vous voulez tirer un résultat, vous appelez une fonction et vous ne faites rien d'autre jusqu'à ce que la fonction revienne.

Vous voulez qu'un résultat soit transmis à vous, vous appelez une fonction asynchrone et attendez le résultat. Lorsque le résultat est disponible, il est poussé vers le rappel, ce qui reprend la méthode là où elle doit être.

IEnumerable<T> est juste une séquence de tirages; vous appelez MoveNext chaque fois que vous voulez obtenir un résultat et vous obtenez T. IObservable<T> est juste une séquence de poussées; vous enregistrez un rappel, et il est appelé chaque fois qu'une nouvelle T est disponible.

En d'autres termes: IEnumerable<T> est logiquement une séquence de Func<T>invocations. IObservable<T> est logiquement une séquence de Task<T>continuations. Ne laissez pas le fait qu’elles soient séquences vous confondre; c'est accessoire. L'idée fondamentale est que les fonctions sont synchrones; vous les appelez et obtenez un résultat synchrone; si cela prend un moment, vous attendez. Les tâches sont asynchrones; vous les démarrez et obtenez le résultat asynchrone quand il est disponible. 


Cette idée existait dans C # avant IObservable et await. Une autre façon de voir les choses est la suivante: le pull s'apparente aux appels de fonctions, le push s'apparente aux gestionnaires d'événements. Un appel de fonction normal, vous l’appelez quand vous voulez quelque chose. Un gestionnaire d'événements, il vous appelle quand quelque chose se passe. Les événements sont la façon dont le langage C # lui-même représente le modèle d'observateur . Mais les événements forment toujours logiquement une séquence , il est donc logique de pouvoir manipuler une séquence d'éléments insérés de la même manière que nous manipulions une séquence d'éléments extraits. Et par conséquent, IObservable a été inventé.

22
Eric Lippert

En supposant un serveur (logique) et un client, qui détermine quand les données sont livrées, le serveur ou le client? 

Pull-based décrit un schéma exécuté par le client: les clients font des demandes et les données sont immédiatement servies. Push-based décrit un schéma exécuté sur le serveur: les clients peuvent se connecter à un flux Push (IObservable), mais ils ne peuvent pas exiger de données, ils les obtiennent lorsque le serveur souhaite les transmettre.

Une forme canonique d'extraction serait une requête dans la base de données: vous envoyez une demande au serveur et le serveur répond avec une collection d'éléments. Une version canonique de Push serait une application de discussion en ligne: le client de discussion en ligne ne peut pas «exiger» de nouvelles données de conversation, le serveur vous avertit lorsque la personne a dit quelque chose.

4
Shlomo

Un homme entre dans une épicerie et demande au commerçant s'il a des œufs. "Oui", dit le commerçant. "Pourrais-je en avoir?" demande l'homme. Et le commerçant donne des œufs à l'homme. "En avez vous encore?" demande l'homme. "Oui", dit le commerçant. "Pourrais-je en avoir?" demande l'homme. Et le commerçant donne des œufs à l'homme. "En avez vous encore?" demande l'homme. "Non", dit le commerçant. L'homme part.

C'est basé sur la traction. L'homme continue à "tirer" les oeufs du commerçant jusqu'à ce qu'il n'en reste plus.

Un homme entre dans une épicerie et demande au commerçant s'il peut livrer des œufs et, le cas échéant, peut le faire à chaque fois qu'il peut les obtenir. "Oui", dit le commerçant. L'homme part. Quelques jours plus tard, des œufs arrivent. Quelques jours plus tard, des œufs arrivent. Ensuite, l'homme appelle le commerçant et demande à ce que les livraisons s'arrêtent. Pas plus d'œufs arrivent.

C'est basé sur Push. L'homme n'attend pas que le commerçant lui donne des œufs. L'homme va faire autre chose et le commerçant "pousse" les œufs vers l'homme.

2
Enigmativity

En plus des bonnes réponses ci-dessus, j'offrirais également ce qui suit:

  • 'IEnumerable' signifiant 'enumerable' est implicitement confondu avec 'pull based' dans le modèle conceptuel du framework .NET. Il est destiné à signifier "vous permet d'obtenir un énumérateur, ce qui vous permet tirer la valeur suivante"
  • Mais nous avons aussi IAsyncEnumerable. 
  • Mathématiquement, énumérable signifie simplement «dénombrable», c’est-à-dire un ensemble dénombrable.
  • Les observables peuvent également être dénombrables, en ce sens qu’ils peuvent représenter un ensemble d’événements discrets.

La dénomination est confuse et à mon avis représente mal les concepts qu’ils entendent. Par exemple, dans le monde Java, IEnumerable est Iterable, ce qui est plus proche de l’intention de .NET.

Je pense qu'il est plus simple d'imaginer IEnumerable et les constructions LINQ autour d'eux comme "Récupérez-moi des données à partir d'une source et filtrez-les/groupez-les comme ceci ..." alors que Observables peut être considéré comme un flux entrant que vous pouvez réagir à. Je ne pense pas qu'il soit nécessairement utile de penser aux observables comme des continuations.

1
Sentinel