Wann wäre eine Abfrage nach Ereignissen besser als die Verwendung eines Beobachtermusters?

41

Gibt es Szenarien, in denen die Abfrage nach Ereignissen besser wäre als die Verwendung des Beobachtermusters ? Ich habe Angst vor der Verwendung von Polling und würde es nur verwenden, wenn mir jemand ein gutes Szenario gibt. Ich kann mir nur vorstellen, wie das Beobachtermuster besser ist als die Befragung. Betrachten Sie dieses Szenario:

Sie programmieren einen Autosimulator. Das Auto ist ein Objekt. Sobald sich das Auto einschaltet, möchten Sie einen Soundclip "vroom vroom" abspielen.

Sie können dies auf zwei Arten modellieren:

Polling : Pollen Sie das Autoobjekt jede Sekunde ab, um festzustellen, ob es eingeschaltet ist. Wenn es eingeschaltet ist, spielen Sie den Soundclip ab.

Beobachtermuster : Machen Sie das Auto zum Motiv des Beobachtermusters. Lassen Sie es das "Ein" -Ereignis für alle Beobachter veröffentlichen, wenn es sich selbst einschaltet. Erstellen Sie ein neues Soundobjekt, das auf das Auto hört. Lass es den "on" Callback implementieren, der den Soundclip abspielt.

In diesem Fall, denke ich, gewinnt das Beobachtermuster. Erstens ist die Abfrage prozessorintensiver. Zweitens wird der Soundclip beim Einschalten des Autos nicht sofort ausgelöst. Aufgrund des Abfragezeitraums kann eine Pause von bis zu 1 Sekunde auftreten.

JoJo
quelle
Mir fällt so gut wie kein Szenario ein. Das Beobachtermuster ist das, was tatsächlich der realen Welt und dem realen Leben entspricht. Daher denke ich, dass kein Szenario es jemals rechtfertigen würde, es nicht zu verwenden.
Saeed Neamati
Sprechen Sie über Ereignisse auf der Benutzeroberfläche oder über Ereignisse im Allgemeinen?
Bryan Oakley
3
Ihr Beispiel beseitigt das Abfrage- / Beobachtungsproblem nicht. Sie haben es einfach an eine niedrigere Ebene übergeben. Ihr Programm muss noch herausfinden, ob das Auto eingeschaltet ist oder nicht.
Eintauchen

Antworten:

55

Stellen Sie sich vor, Sie möchten über jeden Motorzyklus informiert werden, z. B. um dem Fahrer eine Drehzahlmessung anzuzeigen.

Beobachtermuster: Der Motor veröffentlicht für jeden Zyklus ein "Motorzyklus" -Ereignis für alle Beobachter. Erstellen Sie einen Listener, der Ereignisse zählt und die RPM-Anzeige aktualisiert.

Abfrage: Die Drehzahlanzeige fragt den Motor in regelmäßigen Abständen nach einem Motorzykluszähler und aktualisiert die Drehzahlanzeige entsprechend.

In diesem Fall würde sich das Beobachtermuster wahrscheinlich lösen: Der Motorzyklus ist ein hochfrequenter Prozess mit hoher Priorität. Sie möchten diesen Prozess nicht verzögern oder anhalten, nur um eine Anzeige zu aktualisieren. Sie möchten den Thread-Pool auch nicht mit Motorzyklusereignissen überschwemmen.


PS: Ich verwende das Abfragemuster auch häufig in der verteilten Programmierung:

Beobachtermuster: Prozess A sendet eine Nachricht an Prozess B, die besagt, dass "jedes Mal, wenn ein Ereignis E eintritt, eine Nachricht an Prozess A gesendet wird".

Abfragemuster: Prozess A sendet regelmäßig eine Nachricht an Prozess B mit der Meldung "Wenn Ereignis E seit dem letzten Abruf aufgetreten ist, senden Sie mir jetzt eine Nachricht".

Das Abfragemuster führt zu einer etwas höheren Netzwerklast. Aber das Beobachtermuster hat auch Nachteile:

  • Wenn Prozess A abstürzt, wird er sich niemals abmelden, und Prozess B wird versuchen, für alle Ewigkeit Benachrichtigungen an ihn zu senden, es sei denn, er kann Remote-Prozessfehler zuverlässig erkennen (was nicht einfach ist).
  • Wenn Ereignis E sehr häufig ist und / oder die Benachrichtigungen viele Daten enthalten, erhält Prozess A möglicherweise mehr Ereignisbenachrichtigungen, als er verarbeiten kann. Mit dem Abfragemuster kann das Abfragen nur gedrosselt werden.
  • Im Beobachtermuster kann eine hohe Last "Welligkeiten" durch das gesamte System verursachen. Wenn Sie Sperrbuchsen verwenden, können diese Wellen in beide Richtungen verlaufen.
Nikie
quelle
1
Guter Punkt. Manchmal ist es aus Gründen der Leistung auch besser, eine Umfrage durchzuführen.
Falcon
1
Die erwartete Anzahl von Beobachtern ist ebenfalls eine Überlegung. Wenn Sie eine große Anzahl von Beobachtern erwarten, kann die Aktualisierung aller Beobachter zu einem Leistungsengpass werden. Es ist viel einfacher, einfach irgendwo einen Wert zu schreiben und die "Beobachter" diesen Wert prüfen zu lassen, wenn sie ihn benötigen.
Marjan Venema
1
"es sei denn, es kann zuverlässig Remote-Prozessfehler erkennen (was nicht einfach ist)" ... außer durch Abfragen; P. Daher ist es das beste Design, die Reaktion "nichts hat sich geändert" so weit wie möglich zu minimieren. +1, gute Antwort.
pdr
2
@Jojo: Sie könnten, ja, aber dann setzen Sie die Richtlinie, die in der Anzeige angezeigt werden soll, in den RPM-Zähler ein. Möglicherweise möchte der Benutzer gelegentlich eine hochpräzise Drehzahlanzeige haben.
Zan Lynx
2
@JoJo: Jedes 100. Event zu veröffentlichen ist ein Hack. Dies funktioniert nur dann gut, wenn die Ereignisfrequenz immer im richtigen Bereich liegt, wenn die Verarbeitung des Ereignisses für die Engine nicht zu lange dauert und alle Teilnehmer eine vergleichbare Genauigkeit benötigen. Und es wird eine Moduloperation pro U / min benötigt, was (unter der Annahme einiger Tausend U / min) für die CPU viel mehr Arbeit bedeutet als ein paar Abrufoperationen pro Sekunde.
Nikie
7

Polling ist besser, wenn der Polling-Prozess erheblich langsamer abläuft als die Polling-Vorgänge. Wenn Sie Ereignisse in eine Datenbank schreiben, ist es häufig besser, alle Ihre Ereignisproduzenten abzufragen, alle Ereignisse zu erfassen, die seit der letzten Abfrage aufgetreten sind, und sie dann in eine einzige Transaktion zu schreiben. Wenn Sie versuchen, jedes Ereignis so zu schreiben, wie es aufgetreten ist, können Sie möglicherweise nicht mithalten und haben schließlich Probleme, wenn sich Ihre Eingabewarteschlangen füllen. Dies ist auch in lose gekoppelten verteilten Systemen sinnvoller, in denen die Latenzzeit hoch oder der Verbindungsaufbau und -abbau teuer sind. Meiner Meinung nach sind Abfragesysteme einfacher zu schreiben und zu verstehen, aber in den meisten Situationen scheinen Beobachter oder ereignisgesteuerte Verbraucher eine bessere Leistung zu bieten (meiner Erfahrung nach).

TMN
quelle
7

Polling ist viel einfacher, Arbeit zu bekommen über ein Netzwerk , wenn Verbindungen ausfallen können, Server gehen kann getan usw. Denken Sie daran , am Ende des Tages ein TCP - Socket „Polling“ Keep-a-Live - Nachrichten sonst der Server den Client benötigt werden nehmen ist weggegangen.

Polling ist auch dann sinnvoll, wenn Sie eine Benutzeroberfläche auf dem neuesten Stand halten möchten, sich die zugrunde liegenden Objekte jedoch sehr schnell ändern. In den meisten Apps ist es nicht sinnvoll, die Benutzeroberfläche mehr als ein paar Mal pro Sekunde zu aktualisieren.

Vorausgesetzt, der Server kann mit sehr geringen Kosten auf "keine Änderung" antworten, und Sie rufen nicht zu oft ab und es gibt nicht Tausende von Clients, die abrufen, dann funktioniert das Abrufen im wirklichen Leben sehr gut.

Für “ In-Memory ” -Fälle verwende ich jedoch standardmäßig das Beobachtermuster, da dies normalerweise die geringste Arbeit ist.

Ian
quelle
5

Umfragen haben einige Nachteile, die Sie im Grunde genommen bereits in Ihrer Frage angegeben haben.

Es kann jedoch eine bessere Lösung sein, wenn Sie das Observable wirklich von allen Beobachtern entkoppeln möchten. Manchmal ist es in solchen Fällen jedoch besser, einen beobachtbaren Wrapper für das zu beobachtende Objekt zu verwenden.

Ich würde Polling nur verwenden, wenn das Observable bei Objektinteraktionen nicht beobachtet werden kann, was häufig der Fall ist, wenn beispielsweise Datenbanken abgefragt werden, für die keine Rückrufe möglich sind. Ein weiteres Problem kann Multithreading sein, bei dem es oftmals sicherer ist, Nachrichten abzufragen und zu verarbeiten, als Objekte direkt aufzurufen, um Parallelitätsprobleme zu vermeiden.

Falke
quelle
Ich bin mir nicht ganz sicher, warum Sie glauben, dass Abfragen für Multithreading sicherer sind. In den meisten Fällen ist dies nicht der Fall. Wenn der Poll-Handler eine Poll-Anfrage erhält, musste er den Status des abgefragten Objekts ermitteln. Wenn das Objekt gerade aktualisiert wird, ist dies für den Poll-Handler nicht sicher. Im Listener-Szenario erhalten Sie nur Benachrichtigungen, wenn sich der Pusher im konsistenten Zustand befindet, sodass Sie die meisten Synchronisierungsprobleme im abgefragten Objekt vermeiden können.
Lie Ryan
4

Ein gutes Beispiel dafür, wie Abfragen von Benachrichtigungen übernommen werden, finden Sie in den Netzwerkstapeln des Betriebssystems.

Für Linux war es eine große Sache, als der Netzwerkstapel NAPI aktivierte, eine Netzwerk-API, die es den Treibern ermöglichte, von einem Interrupt-Modus (Benachrichtigung) in einen Polling-Modus zu wechseln.

Bei mehreren Gigabit-Ethernet-Schnittstellen überlasteten die Interrupts häufig die CPU und führten dazu, dass das System langsamer lief, als es sollte. Beim Abrufen sammeln die Netzwerkkarten Pakete in Puffern, bis sie abgerufen werden, oder die Karten schreiben die Pakete sogar über DMA in den Speicher. Wenn das Betriebssystem bereit ist, fragt es die Karte nach all ihren Daten ab und führt die Standard-TCP / IP-Verarbeitung durch.

Der Polling-Modus ermöglicht es der CPU, Ethernet-Daten mit maximaler Verarbeitungsrate ohne unnötige Interrupt-Belastung zu erfassen. Der Interrupt-Modus ermöglicht es der CPU, zwischen Paketen im Leerlauf zu sein, wenn die Arbeit nicht so beschäftigt ist.

Das Geheimnis ist, wann von einem Modus in den anderen gewechselt werden muss. Jeder Modus hat Vorteile und sollte an der richtigen Stelle verwendet werden.

Zan Lynx
quelle
2

Ich liebe Umfragen! Mache ich Ja! Mache ich Ja! Mache ich Ja! Mache ich noch Ja! Was ist mit jetzt? Ja!

Wie andere bereits erwähnt haben, kann es unglaublich ineffizient sein, wenn Sie nur abfragen, um immer wieder denselben unveränderten Status zu erhalten. Dies ist ein Rezept, um CPU-Zyklen zu verbrennen und die Akkulaufzeit auf Mobilgeräten erheblich zu verkürzen. Natürlich ist es keine Verschwendung, wenn Sie jedes Mal einen neuen und aussagekräftigen Zustand mit einer Geschwindigkeit zurückerhalten, die nicht schneller als gewünscht ist.

Aber der Hauptgrund, warum ich das Umfragen liebe, ist seine Einfachheit und Berechenbarkeit. Sie können den Code nachverfolgen und leicht sehen, wann und wo Dinge passieren und in welchem ​​Thread. Wenn wir theoretisch in einer Welt leben würden, in der Umfragen eine vernachlässigbare Verschwendung darstellen (obwohl die Realität weit davon entfernt ist), würde dies meiner Meinung nach die Pflege des Codes erheblich vereinfachen. Und das ist der Vorteil von Polling und Pulling, da ich sehe, ob wir die Leistung außer Acht lassen können, obwohl wir dies in diesem Fall nicht tun sollten.

Als ich in der DOS-Ära mit dem Programmieren anfing, drehten sich meine kleinen Spiele um das Polling. Ich habe Assembler-Code aus einem Buch kopiert, das ich im Zusammenhang mit Tastaturinterrupts kaum verstanden habe, und einen Puffer mit Tastaturzuständen gespeichert, an dem meine Hauptschleife immer abrief. Ist die Auf-Taste gedrückt? Nee. Ist die Auf-Taste gedrückt? Nee. Wie wäre es jetzt? Nee. Jetzt? Ja. Okay, beweg den Spieler.

Und obwohl es unglaublich verschwenderisch war, fiel mir dies im Vergleich zu diesen Tagen mit Multitasking und ereignisgesteuerter Programmierung viel leichter. Ich wusste genau, wann und wo die Dinge zu jeder Zeit passieren würden, und es war einfacher, die Bildraten ohne Probleme stabil und vorhersehbar zu halten.

Seitdem habe ich immer versucht, einen Weg zu finden, um einige der Vorteile und Vorhersagbarkeiten davon zu erhalten, ohne die CPU-Zyklen tatsächlich zu verbrennen. Mach ihr Ding und schlafe wieder ein und warte darauf, wieder benachrichtigt zu werden.

Und irgendwie finde ich es viel einfacher, mit Ereignis-Warteschlangen zu arbeiten, als mit Beobachter-Mustern, obwohl sie es immer noch nicht so einfach machen, vorherzusagen, wohin Sie gehen oder was am Ende passieren wird. Sie zentralisieren zumindest den Ablauf der Ereignisbehandlungssteuerung auf einige Schlüsselbereiche im System und behandeln diese Ereignisse immer im selben Thread, anstatt plötzlich von einer Funktion an einen völlig entfernten und unerwarteten Ort außerhalb eines zentralen Ereignisbehandlungs-Threads zu springen. Die Dichotomie muss also nicht immer zwischen Beobachtern und Befragten bestehen. Ereigniswarteschlangen sind dort eine Art Mittelweg.

Aber ja, irgendwie finde ich es viel einfacher, über Systeme nachzudenken, die Dinge tun, die den vorhersehbaren Kontrollabläufen, die ich vor langer Zeit beim Umfragen hatte, in ähnlicher Weise ähneln, und gleichzeitig der Tendenz entgegenzuwirken, dass die Arbeit dort stattfindet Zeiten, in denen keine Zustandsänderungen aufgetreten sind. Es ist also von Vorteil, wenn Sie dies auf eine Weise tun können, bei der CPU-Zyklen nicht unnötig verbrannt werden, wie dies bei Bedingungsvariablen der Fall ist.

Homogene Schleifen

Josh CaswellAlso gut , ich habe einen tollen Kommentar bekommen , der auf einige Dummheiten in meiner Antwort hinwies:

"wie das Verwenden von Bedingungsvariablen, um Threads zum Aufwecken zu benachrichtigen" Klingt wie eine ereignisbasierte / beobachtende Anordnung, keine Abfrage

Technisch gesehen wendet die Bedingungsvariable selbst das Beobachtermuster an, um Threads aufzuwecken / zu benachrichtigen, so dass die Bezeichnung "Polling" wahrscheinlich unglaublich irreführend wäre. Ich stelle jedoch fest, dass es einen ähnlichen Vorteil bietet wie das Abfragen aus DOS-Tagen (nur in Bezug auf Kontrollfluss und Vorhersagbarkeit). Ich werde versuchen, es besser zu erklären.

Was ich damals ansprechend fand, war, dass Sie sich einen Codeabschnitt ansehen oder ihn nachverfolgen und sagen konnten: "Okay, dieser gesamte Abschnitt ist der Behandlung von Tastaturereignissen gewidmet. In diesem Codeabschnitt wird nichts anderes passieren Und ich weiß genau, was vorher passieren wird, und ich weiß genau, was danach passieren wird (z. B. Physik und Rendering). " Durch die Abfrage der Tastaturzustände konnten Sie den Steuerungsfluss so zentralisieren, dass Sie nur noch handhaben konnten, was als Reaktion auf dieses externe Ereignis geschehen sollte. Wir haben nicht sofort auf dieses externe Ereignis reagiert. Wir haben darauf nach Belieben reagiert.

Wenn wir ein Push-basiertes System verwenden, das auf einem Observer-Muster basiert, verlieren wir oft diese Vorteile. Die Größe eines Steuerelements wird möglicherweise geändert, wodurch ein Größenänderungsereignis ausgelöst wird. Wenn wir es durchgehen, stellen wir fest, dass wir uns in einer exotischen Kontrolle befinden, die eine Menge benutzerdefinierter Dinge in ihrer Größenänderung ausführt, die mehr Ereignisse auslöst. Wir sind am Ende völlig überrascht, in all diesen kaskadierenden Ereignissen zu stecken, wo wir im System landen. Darüber hinaus stellen wir möglicherweise fest, dass all dies in keinem bestimmten Thread konsistent auftritt, da Thread A hier möglicherweise die Größe eines Steuerelements ändert, während Thread B später auch die Größe eines Steuerelements ändert. Ich fand es immer sehr schwierig zu überlegen, wie schwierig es ist, vorherzusagen, wo alles passiert und was passieren wird.

Die Ereigniswarteschlange ist für mich etwas einfacher zu überlegen, weil sie vereinfacht, wo all diese Dinge zumindest auf Thread-Ebene passieren. Es könnten jedoch viele unterschiedliche Dinge passieren. Eine Ereigniswarteschlange könnte eine eklektische Mischung von Ereignissen enthalten, die verarbeitet werden müssen, und jedes einzelne könnte uns überraschen, welche Ereigniskaskade aufgetreten ist, in welcher Reihenfolge sie verarbeitet wurden und wie wir am Ende überall in der Codebasis abprallen .

Was ich für "am nächsten am Polling" halte, würde keine Ereigniswarteschlange verwenden, sondern eine sehr homogene Verarbeitung aufschieben. A wird PaintSystemmöglicherweise durch eine Bedingungsvariable darauf aufmerksam gemacht, dass Malarbeiten erforderlich sind, um bestimmte Rasterzellen eines Fensters neu zu zeichnen. An diesem Punkt führt es eine einfache sequenzielle Schleife durch die Zellen durch und zeichnet alles in der richtigen z-Reihenfolge neu. Möglicherweise gibt es hier eine Ebene für den Aufruf von Indirection / Dynamic Dispatch, um die Paint-Ereignisse in jedem Widget auszulösen, das sich in einer Zelle befindet, die neu gezeichnet werden muss, aber das ist es - nur eine Ebene für indirekte Aufrufe. Die Bedingungsvariable verwendet das Beobachtermuster, um die PaintSystemAufgabe zu warnen, gibt jedoch nicht mehr als das anPaintSystemwidmet sich zu diesem Zeitpunkt einer einheitlichen, sehr homogenen Aufgabe. Wenn wir den PaintSystem'sCode debuggen und nachverfolgen , wissen wir, dass nichts anderes als Malen passieren wird.

Es geht also hauptsächlich darum, das System dahin zu bringen, wo diese Dinge homogene Schleifen über Daten ausführen, und dabei eine sehr singuläre Verantwortung zu übernehmen, anstatt inhomogene Schleifen über unterschiedliche Datentypen, die zahlreiche Aufgaben ausführen, wie dies bei der Verarbeitung von Ereigniswarteschlangen der Fall sein kann.

Wir streben diese Art von Dingen an:

when there's work to do:
   for each thing:
       apply a very specific and uniform operation to the thing

Im Gegensatz zu:

when one specific event happens:
    do something with relevant thing
in relevant thing's event:
    do some more things
in thing1's triggered by thing's event:
    do some more things
in thing2's event triggerd by thing's event:
    do some more things:
in thing3's event triggered by thing2's event:
    do some more things
in thing4's event triggered by thing1's event:
    cause a side effect which shouldn't be happening
    in this order or from this thread.

Und so weiter. Und es muss nicht ein Thread pro Aufgabe sein. Ein Thread wendet möglicherweise Layout-Logik (Größenänderung / Neupositionierung) für GUI-Steuerelemente an und zeichnet sie neu, kann jedoch Tastatur- oder Mausklicks nicht verarbeiten. Sie können dies als Verbesserung der Homogenität einer Ereigniswarteschlange betrachten. Wir müssen jedoch keine Ereigniswarteschlange verwenden und auch keine Funktionen zum Ändern der Größe und zum Zeichnen überlappen. Wir können machen wie:

in thread dedicated to layout and painting:
    when there's work to do:
         for each widget that needs resizing/reposition:
              resize/reposition thing to target size/position
              mark appropriate grid cells as needing repainting
         for each grid cell that needs repainting:
              repaint cell
         go back to sleep

Der obige Ansatz verwendet nur eine Bedingungsvariable, um den Thread zu benachrichtigen, wenn Arbeit zu erledigen ist, verschachtelt jedoch nicht verschiedene Arten von Ereignissen (Größe in einer Schleife ändern, in einer anderen Schleife malen, nicht eine Mischung aus beiden), und dies ist nicht der Fall. ' Ich muss nur mitteilen, was genau die Arbeit ist, die erledigt werden muss (der Thread "entdeckt" dies beim Aufwachen, indem er sich die systemweiten Zustände des ECS ansieht). Jede durchgeführte Schleife ist dann von Natur aus sehr homogen, was es einfach macht, über die Reihenfolge, in der alles geschieht, nachzudenken.

Ich bin mir nicht sicher, wie ich diesen Ansatz bezeichnen soll. Ich habe nicht gesehen, dass andere GUI-Engines dies tun, und es ist meine eigene exotische Herangehensweise an meine. Aber bevor ich versuchte, Multithread-GUI-Frameworks mithilfe von Beobachtern oder Ereigniswarteschlangen zu implementieren, hatte ich enorme Probleme beim Debuggen und stieß auch auf einige unklare Rennbedingungen und Deadlocks, die ich nicht klug genug war, um sie auf eine Weise zu beheben, die mich zuversichtlich machte über die Lösung (einige Leute könnten dies tun, aber ich bin nicht schlau genug). Mein erstes Iterationsdesign nannte einen Slot direkt durch ein Signal, und einige Slots brachten dann andere Threads hervor, um asynchrone Arbeit zu leisten. Das war am schwierigsten zu überlegen, und ich stolperte über Rennbedingungen und Deadlocks. Bei der zweiten Iteration wurde eine Ereigniswarteschlange verwendet. aber nicht leicht genug für mein Gehirn, es zu tun, ohne immer noch in die dunkle Sackgasse und in den Rennzustand zu geraten. Die dritte und letzte Iteration verwendete den oben beschriebenen Ansatz und ermöglichte es mir schließlich, ein Multithread-GUI-Framework zu erstellen, das selbst ein dummer Simpleton wie ich korrekt implementieren konnte.

Dann erlaubte mir diese Art des endgültigen Multithread-GUI-Designs, etwas anderes zu erfinden, über das ich viel einfacher nachdenken und die Art von Fehlern vermeiden konnte, die ich tendenziell machte, und einen der Gründe, warum ich es so viel einfacher fand, über das ich nachdenken konnte Am wenigsten liegt es an diesen homogenen Schleifen und daran, wie sie dem Kontrollfluss ähnelten, als ich in den DOS-Tagen abgefragt habe (obwohl es nicht wirklich abgefragt wird und nur dann Arbeit leistet, wenn noch Arbeit zu erledigen ist). Die Idee war, sich so weit wie möglich vom Ereignisbehandlungsmodell zu entfernen, das inhomogene Schleifen, inhomogene Nebenwirkungen und inhomogene Kontrollflüsse impliziert, und immer mehr auf homogene Schleifen hinzuarbeiten, die einheitlich mit homogenen Daten arbeiten und isolieren und vereinheitlichen von Nebenwirkungen auf eine Weise, die es einfacher machte, sich nur auf das zu konzentrieren, was


quelle
1
"wie das Verwenden von Bedingungsvariablen, um Threads zum Aufwecken zu benachrichtigen" Klingt wie eine ereignisbasierte / beobachtende Anordnung, keine Abfrage.
Josh Caswell
Ich finde den Unterschied sehr subtil, aber die Benachrichtigung erfolgt nur in Form von "Es muss noch gearbeitet werden", um Threads aufzuwecken. Beispielsweise kann ein Beobachter-Muster beim Ändern der Größe eines übergeordneten Steuerelements die Größe von Aufrufen kaskadenartig ändern und dabei die Hierarchie mithilfe der dynamischen Verteilung herunterfahren. Die Funktionen zum Ändern der Größe eines Ereignisses werden indirekt sofort aufgerufen. Dann könnten sie sich sofort neu streichen. Wenn Sie dann eine Ereigniswarteschlange verwenden, wird durch das Ändern der Größe eines übergeordneten Steuerelements möglicherweise die Größe von Ereignissen in der Hierarchie nach unten verschoben. Zu diesem Zeitpunkt werden die Größenänderungsfunktionen für jedes Steuerelement möglicherweise verzögert aufgerufen.
... darauf hinweisen, dass sie dann möglicherweise Ereignisse verschieben, die ebenfalls verzögert aufgerufen werden, nachdem die Größenänderung abgeschlossen ist, und zwar von einem zentralen Thread für die Ereignisbehandlung. Und ich finde, dass die Zentralisierung ein wenig hilfreich ist, zumindest was das Debuggen und die Möglichkeit anbelangt, leicht nachzuvollziehen, wo die Verarbeitung stattfindet (einschließlich des Threads). Was ich dann für am nächsten an der Abfrage halte, ist keines von beiden Diese Lösungen ...
Es wäre zum Beispiel ein, LayoutSystemder normalerweise schläft, aber wenn der Benutzer die Größe eines Steuerelements ändert, würde er eine Bedingungsvariable verwenden, um das Steuerelement aufzuwecken LayoutSystem. Dann LayoutSystemändert die Größe aller erforderlichen Steuerelemente und geht wieder in den Ruhezustand. Dabei werden die rechteckigen Bereiche, in denen sich die Widgets befinden, als aktualisierungsbedürftig markiert. Zu diesem Zeitpunkt wird A PaintSystemaktiviert und durchläuft diese rechteckigen Bereiche. Dabei werden die neu gezeichnet, die in einer flachen sequenziellen Schleife neu gezeichnet werden müssen.
Die Bedingungsvariable selbst folgt also einem Beobachtermuster, um Threads zu benachrichtigen, die aufgeweckt werden sollen, aber wir übertragen keine Informationen mehr als "es ist noch Arbeit zu erledigen". Und jedes System, das aufwacht, ist dazu bestimmt, Dinge in einer sehr einfachen Schleife zu verarbeiten, wobei eine sehr homogene Aufgabe angewendet wird, im Gegensatz zu einer Ereigniswarteschlange, die inhomogene Aufgaben hat (sie kann eine eklektische Mischung von zu verarbeitenden Ereignissen enthalten).
-4

Ich gebe Ihnen einen Überblick über das konzeptionelle Denken über das Betrachtermuster. Denken Sie an ein Szenario wie das Abonnieren eines YouTube-Kanals. Es gibt eine Anzahl von Benutzern, die den Kanal abonnieren, und sobald der Kanal aktualisiert wird, der aus vielen Videos besteht, wird der Abonnent benachrichtigt, dass in diesem bestimmten Kanal eine Änderung vorliegt. Wir sind daher zu dem Schluss gekommen, dass, wenn der Kanal SUBJECT ist, der die Möglichkeit hat, sich anzumelden, alle im Kanal registrierten BEOBACHTER abgemeldet und benachrichtigt werden.

Aroop Bhattacharya
quelle
2
Dies versucht nicht einmal, die gestellte Frage zu beantworten, wann eine Abfrage nach Ereignissen besser ist als die Verwendung eines Beobachtermusters. Siehe Wie man antwortet
Mücke