Ich bin derzeit mit dem Reactive Extensions-Framework für .NET vertraut und arbeite mich durch die verschiedenen Einführungsressourcen, die ich gefunden habe (hauptsächlich http://www.introtorx.com ).
Unsere Anwendung umfasst eine Reihe von Hardwareschnittstellen, die Netzwerkrahmen erkennen. Dies sind meine IObservables. Ich habe dann eine Vielzahl von Komponenten, die diese Rahmen verbrauchen oder eine Art Transformation für die Daten durchführen und einen neuen Rahmentyp erzeugen. Es wird auch andere Komponenten geben, die beispielsweise jeden n-ten Frame anzeigen müssen. Ich bin überzeugt, dass Rx für unsere Anwendung nützlich sein wird, habe jedoch Probleme mit den Implementierungsdetails für die IObserver-Schnittstelle.
Die meisten (wenn nicht alle) Ressourcen, die ich gelesen habe, haben gesagt, dass ich die IObservable-Schnittstelle nicht selbst implementieren sollte, sondern eine der bereitgestellten Funktionen oder Klassen verwenden sollte. Aus meiner Forschung geht hervor, dass das Erstellen eines Subject<IBaseFrame>
mir das bietet, was ich brauche. Ich hätte meinen einzelnen Thread, der Daten von der Hardwareschnittstelle liest und dann die OnNext-Funktion meiner Subject<IBaseFrame>
Instanz aufruft . Die verschiedenen IObserver-Komponenten würden dann ihre Benachrichtigungen von diesem Betreff erhalten.
Meine Verwirrung ergibt sich aus den Ratschlägen im Anhang dieses Tutorials, in denen es heißt:
Vermeiden Sie die Verwendung der Betreffarten. Rx ist effektiv ein funktionales Programmierparadigma. Die Verwendung von Subjekten bedeutet, dass wir jetzt den Status verwalten, der möglicherweise mutiert. Es ist sehr schwierig, sich gleichzeitig mit dem Mutationsstatus und der asynchronen Programmierung zu befassen. Darüber hinaus wurden viele der Operatoren (Erweiterungsmethoden) sorgfältig geschrieben, um sicherzustellen, dass die korrekte und konsistente Lebensdauer von Abonnements und Sequenzen erhalten bleibt. Wenn Sie Themen einführen, können Sie dies unterbrechen. In zukünftigen Versionen kann es auch zu erheblichen Leistungseinbußen kommen, wenn Sie explizit Themen verwenden.
Meine Anwendung ist sehr leistungskritisch. Ich werde natürlich die Leistung der Verwendung der Empfangsmuster testen, bevor sie in den Produktionscode eingeht. Ich mache mir jedoch Sorgen, dass ich mit der Subject-Klasse etwas tue, das gegen den Geist des Rx-Frameworks verstößt, und dass eine zukünftige Version des Frameworks die Leistung beeinträchtigen wird.
Gibt es eine bessere Möglichkeit, das zu tun, was ich will? Der Hardware-Polling-Thread wird kontinuierlich ausgeführt, unabhängig davon, ob Beobachter vorhanden sind oder nicht (der HW-Puffer wird ansonsten gesichert). Dies ist also eine sehr heiße Sequenz. Ich muss dann die empfangenen Frames an mehrere Beobachter weitergeben.
Jeder Rat wäre sehr dankbar.
quelle
Antworten:
Ok, wenn wir meine dogmatischen Wege ignorieren und "Themen sind gut / schlecht" alle zusammen ignorieren. Schauen wir uns den Problembereich an.
Ich wette, Sie haben entweder 1 von 2 Systemstilen, in die Sie sich integrieren müssen.
Für Option 1, einfach, wickeln wir es einfach mit der entsprechenden FromEvent-Methode ein und wir sind fertig. Zum Pub!
Für Option 2 müssen wir nun überlegen, wie wir dies abfragen und wie dies effizient durchgeführt werden kann. Wie veröffentlichen wir den Wert, wenn wir ihn erhalten?
Ich würde mir vorstellen, dass Sie einen dedizierten Thread für die Abfrage wünschen. Sie möchten nicht, dass ein anderer Codierer den ThreadPool / TaskPool hämmert und Sie in einer ThreadPool-Hungersituation zurücklässt. Alternativ möchten Sie nicht den Aufwand des Kontextwechsels (ich denke). Nehmen wir also an, wir haben unseren eigenen Thread, wir werden wahrscheinlich eine Art While / Sleep-Schleife haben, in der wir sitzen, um abzufragen. Wenn die Prüfung einige Nachrichten findet, veröffentlichen wir sie. Nun, all dies klingt perfekt für Observable.Create. Jetzt können wir wahrscheinlich keine While-Schleife verwenden, da wir dadurch niemals ein Einwegprodukt zurückgeben können, um die Stornierung zu ermöglichen. Zum Glück haben Sie das ganze Buch gelesen und sind mit der rekursiven Planung vertraut!
Ich stelle mir vor, so etwas könnte funktionieren. #Nicht getestet
Der Grund, warum ich Themen wirklich nicht mag, ist, dass der Entwickler normalerweise kein klares Design für das Problem hat. Hacken Sie ein Thema ein, stecken Sie es hier und überall hin und lassen Sie dann den armen Support-Entwickler bei WTF raten. Wenn Sie die Methoden Create / Generate usw. verwenden, lokalisieren Sie die Auswirkungen auf die Sequenz. Sie können alles auf eine Weise sehen und Sie wissen, dass niemand sonst eine böse Nebenwirkung hat. Wenn ich ein Fachfeld sehe, muss ich jetzt nach allen Stellen in einer Klasse suchen, die es verwendet. Wenn ein MFer einen öffentlich macht, sind alle Wetten ungültig, wer weiß, wie diese Sequenz verwendet wird! Async / Concurrency / Rx ist schwierig. Sie müssen es nicht schwieriger machen, indem Sie zulassen, dass Nebenwirkungen und Kausalitätsprogrammierung Ihren Kopf noch mehr drehen.
quelle
Im Allgemeinen sollten Sie die Verwendung vermeiden
Subject
, aber für das, was Sie hier tun, denke ich, dass sie recht gut funktionieren. Ich habe eine ähnliche Frage gestellt, als ich in Rx-Tutorials auf die Meldung "Themen vermeiden" stieß.Um Dave Sexton (von Rxx) zu zitieren
Ich neige dazu, sie als Einstiegspunkt in Rx zu verwenden. Wenn ich also einen Code habe, der sagen muss, dass etwas passiert ist (wie Sie), würde ich a verwenden
Subject
und anrufenOnNext
. Stellen Sie dies dannIObservable
zur Verfügung, damit andere es abonnieren können (Sie können esAsObservable()
für Ihr Thema verwenden, um sicherzustellen, dass niemand ein Thema bearbeiten und die Dinge durcheinander bringen kann).Sie können dies auch mit einem .NET-Ereignis erreichen und verwenden
FromEventPattern
. Wenn ich das Ereignis jedoch nur in ein Ereignis verwandelnIObservable
möchte, sehe ich keinen Vorteil darin, ein Ereignis anstelle eines Ereignisses zu habenSubject
(was bedeuten könnte, dass ich vermisse etwas hier)Was Sie jedoch unbedingt vermeiden sollten, ist das Abonnieren von a
IObservable
mit aSubject
, dh nicht das Übergeben von aSubject
an dieIObservable.Subscribe
Methode.quelle
Wenn Sie einen Betreff verwalten, implementieren Sie häufig nur Funktionen, die bereits in Rx vorhanden sind, und wahrscheinlich nicht so robust, einfach und erweiterbar.
Wenn Sie versuchen, einen asynchronen Datenfluss in Rx anzupassen (oder einen asynchronen Datenfluss aus einem zu erstellen, der derzeit nicht asynchron ist), sind die häufigsten Fälle normalerweise:
Die Datenquelle ist ein Ereignis : Wie Lee sagt, ist dies der einfachste Fall: Verwenden Sie FromEvent und gehen Sie in die Kneipe.
Die Datenquelle ist ein synchroner Vorgang, und Sie möchten abgefragte Aktualisierungen (z. B. einen Webservice oder einen Datenbankaufruf): In diesem Fall können Sie den von Lee vorgeschlagenen Ansatz verwenden, oder in einfachen Fällen können Sie so etwas wie verwenden
Observable.Interval.Select(_ => <db fetch>)
. Möglicherweise möchten Sie DistinctUntilChanged () verwenden, um das Veröffentlichen von Updates zu verhindern, wenn sich an den Quelldaten nichts geändert hat.Die Datenquelle ist eine Art asynchrone API, die Ihren Rückruf aufruft : Verwenden Sie in diesem Fall Observable.Create, um Ihren Rückruf zu verbinden und OnNext / OnError / OnComplete für den Beobachter aufzurufen.
Die Datenquelle ist ein Aufruf, der blockiert, bis neue Daten verfügbar sind (z. B. einige synchrone Socket-Lesevorgänge): In diesem Fall können Sie Observable.Create verwenden, um den imperativen Code zu verpacken, der aus dem Socket liest und auf Observer.OnNext veröffentlicht wenn Daten gelesen werden. Dies ähnelt möglicherweise dem, was Sie mit dem Betreff tun.
Die Verwendung von Observable.Create im Vergleich zum Erstellen einer Klasse, die einen Betreff verwaltet, entspricht in etwa der Verwendung des Schlüsselworts yield im Vergleich zum Erstellen einer ganzen Klasse, die IEnumerator implementiert. Natürlich können Sie einen IEnumerator schreiben, der so sauber und bürgerlich ist wie der Ertragscode, aber welcher ist besser gekapselt und fühlt sich ordentlicher an? Gleiches gilt für Observable.Create vs. Managing Subjects.
Observable.Create bietet Ihnen ein sauberes Muster für die verzögerte Einrichtung und den sauberen Abbau. Wie erreichen Sie dies mit einer Klasse, die ein Thema umschließt? Sie benötigen eine Art Start-Methode ... woher wissen Sie, wann Sie sie aufrufen müssen? Oder starten Sie es einfach immer, auch wenn niemand zuhört? Und wenn Sie fertig sind, wie können Sie es dazu bringen, nicht mehr aus dem Socket zu lesen / die Datenbank abzufragen usw.? Sie müssen über eine Art Stop-Methode verfügen und weiterhin Zugriff nicht nur auf das IObservable haben, das Sie abonniert haben, sondern auch auf die Klasse, die den Betreff erstellt hat.
Mit Observable.Create ist alles an einem Ort zusammengefasst. Der Hauptteil von Observable.Create wird erst ausgeführt, wenn jemand ihn abonniert hat. Wenn also niemand abonniert, verwenden Sie Ihre Ressource niemals. Und Observable.Create gibt ein Disposable zurück, mit dem Ihre Ressourcen / Rückrufe usw. sauber heruntergefahren werden können. Dies wird aufgerufen, wenn der Observer sich abmeldet. Die Lebensdauer der Ressourcen, die Sie zum Generieren des Observable verwenden, hängt eng mit der Lebensdauer des Observable selbst zusammen.
quelle
Der zitierte Blocktext erklärt ziemlich genau, warum Sie ihn nicht verwenden sollten
Subject<T>
, aber um es einfacher auszudrücken, Sie kombinieren die Funktionen von Beobachter und Beobachtbarem, während Sie dazwischen einen Zustand einfügen (unabhängig davon, ob Sie kapseln oder erweitern).Hier geraten Sie in Schwierigkeiten. Diese Verantwortlichkeiten sollten getrennt und voneinander getrennt sein.
In Ihrem speziellen Fall würde ich Ihnen jedoch empfehlen, Ihre Bedenken in kleinere Teile aufzuteilen.
Erstens haben Sie Ihren Thread, der heiß ist, und überwachen die Hardware immer auf Signale, für die Benachrichtigungen ausgelöst werden sollen. Wie würden Sie das normalerweise machen? Ereignisse . Beginnen wir also damit.
Definieren wir,
EventArgs
dass Ihr Ereignis ausgelöst wird.Nun die Klasse, die das Ereignis auslösen wird. Beachten Sie , könnte dies eine statische Klasse sein (da Sie immer ein Thread läuft die Hardware - Pufferüberwachung), oder etwas , das Sie rufen On-Demand , die abonniert hat, dass . Sie müssen dies entsprechend ändern.
Jetzt haben Sie eine Klasse, die ein Ereignis verfügbar macht. Observables funktionieren gut mit Ereignissen. So sehr, dass es erstklassige Unterstützung für die Konvertierung von Ereignisströmen (stellen Sie sich einen Ereignisstrom als mehrere Auslöser eines Ereignisses vor) in
IObservable<T>
Implementierungen gibt, wenn Sie dem Standardereignismuster folgen, und zwar über die statischeFromEventPattern
Methode für dieObservable
Klasse .Mit der Quelle Ihrer Ereignisse und der
FromEventPattern
Methode können wir aufIObservable<EventPattern<BaseFrameEventArgs>>
einfache Weise eine erstellen (dieEventPattern<TEventArgs>
Klasse verkörpert, was Sie in einem .NET-Ereignis sehen würden, insbesondere eine Instanz, die vonEventArgs
einem Absender abgeleitet ist, und ein Objekt, das den Absender darstellt):Natürlich möchten Sie eine
IObservable<IBaseFrame>
, aber das ist einfach, indem Sie dieSelect
Erweiterungsmethode für dieObservable
Klasse verwenden, um eine Projektion zu erstellen (genau wie in LINQ, und wir können all dies in einer benutzerfreundlichen Methode zusammenfassen):quelle
IObservable<T>
da keine Informationen darüber vorliegen, wie Sie Es wird derzeit eine Signalisierung mit diesen Informationen gegeben.Es ist schlecht zu verallgemeinern, dass Themen nicht gut für eine öffentliche Schnittstelle verwendet werden können. Es ist sicherlich richtig, dass ein reaktiver Programmieransatz nicht so aussehen sollte, aber es ist definitiv eine gute Option zur Verbesserung / Umgestaltung Ihres klassischen Codes.
Wenn Sie eine normale Eigenschaft mit einem öffentlichen Set-Accessor haben und über Änderungen benachrichtigen möchten, spricht nichts dagegen, sie durch ein BehaviorSubject zu ersetzen. INPC oder andere andere Ereignisse sind einfach nicht so sauber und es macht mich persönlich fertig. Zu diesem Zweck können und sollten Sie BehaviorSubjects als öffentliche Eigenschaften anstelle von normalen Eigenschaften verwenden und INPC oder andere Ereignisse aus dem Weg räumen.
Darüber hinaus macht die Betreff-Oberfläche die Benutzer Ihrer Benutzeroberfläche auf die Funktionalität Ihrer Eigenschaften aufmerksam und abonniert sie eher, als nur den Wert zu erhalten.
Es ist am besten zu verwenden, wenn andere Änderungen einer Eigenschaft abhören / abonnieren sollen.
quelle