it-swarm.com.de

Was ist der Unterschied zwischen Push-basierten und Pull-basierten Strukturen wie IEnumerable <T> und IObservable <T>?

In jedem Tech-Talk oder in jedem Blogpost habe ich über IEnumerable und IObservable gelesen. Ich habe gelesen, dass IEnumerable eine Pull-basierte Struktur ist und IObservable ist eine Push-basierte Struktur. 

Ich habe gelesen, dass mitIObservablewir async-Aufrufe haben, bei denen nichts blockiert ist und alles auf Push basiert.

Aber, aber, aber ...

Was heißt das wirklich?Pushbased undpullbased

Da meiner Meinung nach in IEnumerable wir auch Daten in die Struktur hineinschieben und auch Daten daraus holen können, habe ich diese technischen Begriffe und Ideen wirklich verloren.

Bitte erklären Sie mir auf normale und menschliche Weise den Unterschied zwischen diesen beiden Strukturen und den Unterschied zwischen Push-basierten und Pull-basierten Strukturen.

Vielen Dank.

16

Bitte erklären Sie mir den Unterschied auf normale und menschliche Weise

OK, lass uns einen Toast machen. Das ist das normalste menschliche Ding, das ich tue.

Pull basiert:

// 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-basiert:

// 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.

Sie möchten ein Ergebnis abrufen, rufen eine Funktion auf und tun nichts anderes, bis die Funktion zurückkehrt.

Sie möchten ein Ergebnis an Sie schieben, Sie rufen eine asynchrone Funktion auf und warten auf das Ergebnis. Wenn das Ergebnis verfügbar ist, wird es beim Callback gedrückt, wodurch die Methode dort wieder aufgenommen wird, wo sie benötigt wird.

IEnumerable<T> ist nur eine Folge von Zügen. Sie rufen MoveNext jedes Mal an, wenn Sie ein Ergebnis abrufen möchten, und Sie erhalten ein TIObservable<T> ist nur eine Folge von Pushs; Sie registrieren einen Rückruf und er wird jedes Mal aufgerufen, wenn ein neues T verfügbar ist.

Anders ausgedrückt: IEnumerable<T> ist logisch eine Folge von Func<T>invocations. IObservable<T> ist logisch eine Folge von Task<T>Fortsetzungen. Lassen Sie sich nicht durch die Tatsache, dass es sich um Sequenzen handelt, verwirren. das ist zufällig. Die Grundidee ist, dass Funktionen synchron sind; Sie rufen sie an und erhalten ein Ergebnis synchron; Wenn es eine Weile dauert, warten Sie. Aufgaben sind asynchron. Sie starten sie und erhalten das Ergebnis asynchron, wenn es verfügbar ist. 


Diese Idee bestand in C # vor IObservable und await. Eine andere Betrachtungsweise ist: Pull-basiert ist wie Funktionsaufrufe, Push-basiert ist wie Ereignishandler. Ein normaler Funktionsaufruf, den Sie aufrufen, wenn Sie etwas möchten. Ein Event-Handler, der Sie anruft, wenn etwas passiert. Ereignisse sind, wie die C # -Sprache selbst das Beobachtermuster darstellt . Ereignisse bilden jedoch immer logisch eine -Sequenz. Daher ist es sinnvoll, eine Sequenz von Push-Elementen auf dieselbe Weise zu manipulieren, wie wir eine Sequenz von Pull-Elementen ändern würden. Und so wurde IObservable erfunden.

22
Eric Lippert

Angenommen, ein (logischer) Server und ein Client, wer bestimmt, wann Daten geliefert werden, der Server oder der Client? 

Pull-basiert beschreibt ein Client-Run-Schema: Clients stellen Anforderungen, und die Daten werden sofort bereitgestellt. Push-basiert beschreibt ein Server-Laufschema: Clients können eine Verbindung zu einem Push-Stream (IObservable) herstellen, sie können jedoch keine Daten anfordern, sie erhalten sie, wenn der Server sie geben möchte.

Eine kanonische Form von Pull wäre eine Datenbankabfrage: Sie senden eine Anfrage an den Server, und der Server antwortet mit einer Sammlung von Elementen. Eine kanonische Version von Push wäre eine Chat-App: Der Chat-Client kann keine neuen Gesprächsdaten anfordern, der Server sagt Ihnen, wenn die andere Person etwas gesagt hat.

4
Shlomo

Ein Mann geht in ein Lebensmittelgeschäft und fragt den Ladenbesitzer, ob er Eier hat. "Ja", sagt der Ladenbesitzer. "Kann ich welche haben?" fragt den Mann. Und der Ladenbesitzer gibt dem Mann Eier. "Hast du noch mehr?" fragt den Mann. "Ja", sagt der Ladenbesitzer. "Kann ich welche haben?" fragt den Mann. Und der Ladenbesitzer gibt dem Mann Eier. "Hast du noch mehr?" fragt den Mann. "Nein", sagt der Ladenbesitzer. Der Mann geht.

Das ist Pull-basiert. Der Mann "zieht" ständig Eier vom Ladenbesitzer, bis keine mehr übrig waren.

Ein Mann geht in ein Lebensmittelgeschäft und fragt den Ladenbesitzer, ob er Eier liefern kann, und wenn ja, kann er sie dann liefern, wann immer er sie bekommen kann. "Ja", sagt der Ladenbesitzer. Der Mann geht. In wenigen Tagen kommen einige Eier an. Ein paar Tage später kommen einige Eier an. Dann ruft der Mann den Ladenbesitzer an und bittet die Auslieferung der Lieferungen. Es kommen keine Eier mehr.

Das ist Push-basiert. Der Mann wartet nicht darauf, dass der Ladenbesitzer ihm Eier gibt. Der Mann macht etwas anderes und der Ladenbesitzer "schiebt" die Eier zu dem Mann.

2
Enigmativity

Zusätzlich zu den oben genannten großartigen Antworten würde ich Folgendes anbieten:

  • 'IEnumerable', was 'Enumerable' bedeutet, wird implizit mit 'Pull-basiert' im konzeptionellen Modell von .NET Framework verwechselt. Es soll bedeuten "Sie können einen Enumerator erhalten, der Ihnen Pull den nächsten Wert ermöglicht"
  • Wir haben aber auch IAsyncEnumerable. 
  • Mathematisch bedeutet Aufzählungszeichen nur "abzählbar", d. H. Eine abzählbare Menge.
  • Observables können auch zählbar sein in dem Sinne, dass ein Observable eine Menge diskreter Ereignisse darstellen kann.

Die Benennung ist verwirrt und repräsentiert meiner Meinung nach die Konzepte, die sie beabsichtigen. Beispielsweise ist in der Java-Welt IEnumerable Iterable, was der Absicht in .NET näher kommt.

Ich denke, es ist am einfachsten, sich IEnumerable vorzustellen und die LINQ-Konstrukte um sie herum als "Holen Sie mir einige Daten aus einer Quelle und filtern/gruppieren Sie sie so ..." reagieren auf. Ich halte es nicht unbedingt für hilfreich, Observablen als Fortsetzungen zu betrachten.

1
Sentinel