Ich versuche zu verstehen, was eine Ereignisschleife ist. Oft ist die Erklärung, dass Sie in einer Ereignisschleife so lange etwas tun, bis Sie benachrichtigt werden, dass ein Ereignis aufgetreten ist. Sie behandeln dann das Ereignis und setzen fort, was Sie zuvor getan haben.
Um die obige Definition mit einem Beispiel abzubilden. Ich habe einen Server, der in einer Ereignisschleife 'lauscht', und wenn eine Socket-Verbindung erkannt wird, werden die Daten von diesem Server gelesen und angezeigt, wonach der Server wie zuvor mit dem Abhören fortfährt / beginnt.
Es ist jedoch zu viel für mich, wenn dieses Ereignis passiert und wir einfach so benachrichtigt werden. Sie können sagen: "Es ist nicht einfach so, dass Sie einen Ereignis-Listener registrieren müssen." Was aber ein Ereignis-Listener ist, ist eine Funktion, die aus irgendeinem Grund nicht zurückkehrt. Befindet es sich in einer eigenen Schleife und wartet darauf, benachrichtigt zu werden, wenn ein Ereignis eintritt? Sollte der Ereignis-Listener auch einen Ereignis-Listener registrieren? Wo hört es auf?
Ereignisse sind eine nette Abstraktion, mit der man arbeiten kann, jedoch nur eine Abstraktion. Ich bin der Meinung, dass Abstimmungen am Ende unvermeidlich sind. Vielleicht machen wir das nicht in unserem Code, aber die niedrigeren Ebenen (die Implementierung der Programmiersprache oder das Betriebssystem) machen das für uns.
Es kommt im Grunde genommen auf den folgenden Pseudocode an, der irgendwo niedrig genug ist, so dass es nicht zu beschäftigtem Warten kommt:
while(True):
do stuff
check if event has happened (poll)
do other stuff
Dies ist mein Verständnis der ganzen Idee und ich würde gerne hören, ob dies richtig ist. Ich bin offen dafür zu akzeptieren, dass die ganze Idee von Grund auf falsch ist. In diesem Fall möchte ich die richtige Erklärung.
quelle
EventSource
tun, wenn die Tastatureingabe nicht abgefragt wird?Antworten:
Die meisten Ereignisschleifen werden angehalten, wenn keine Ereignisse bereitstehen. Dies bedeutet, dass das Betriebssystem dem Task keine Ausführungszeit gibt, bis ein Ereignis eintritt.
Angenommen, das Ereignis ist eine gedrückte Taste. Sie werden möglicherweise gefragt, ob im Betriebssystem eine Schleife vorhanden ist, die nach Tastendrücken sucht. Die Antwort ist nein. Das Drücken von Tasten erzeugt einen Interrupt , der von der Hardware asynchron behandelt wird. Ebenso für Timer, Mausbewegungen, ein ankommendes Paket usw.
Tatsächlich ist das Abfragen von Ereignissen für die meisten Betriebssysteme die Abstraktion. Die Hardware und das Betriebssystem verarbeiten Ereignisse asynchron und stellen sie in eine Warteschlange, die von Anwendungen abgefragt werden kann. In eingebetteten Systemen sieht man nur echtes Polling auf Hardware-Ebene, und auch dort nicht immer.
quelle
Most event loops will block
. Wie fügt sich dies in das "Paradigma der Ereignisschleife ein, das im Gegensatz zur Verwendung von Threads nicht blockierende asynchrone Aufrufe verwendet"?Ich stelle mir einen Event-Listener nicht als eine Funktion vor, die eine eigene Schleife ausführt, sondern als ein Staffellauf, bei dem der erste Läufer auf die Startpistole wartet. Ein wichtiger Grund für die Verwendung von Ereignissen anstelle von Abfragen besteht darin, dass sie bei CPU-Zyklen effizienter sind. Warum? Schau es dir von der Hardware aus an (und nicht vom Quellcode aus).
Betrachten Sie einen Webserver. Wenn Ihr Server aufruft
listen()
und blockiert, tritt Ihr Code als Relay-Runner an seine Stelle. Wenn das erste Paket einer neuen Verbindung eintrifft, startet die Netzwerkkarte das Rennen, indem sie das Betriebssystem unterbricht. Das Betriebssystem führt eine Interrupt-Service-Routine (ISR) aus, die das Paket erfasst. Der ISR übergibt den Staffelstab an eine übergeordnete Routine, die die Verbindung herstellt. Sobald die Verbindung hergestellt ist, gibt diese Routine den Taktstock an weiterlisten()
, der den Taktstock an Ihren Code weitergibt. An diesem Punkt können Sie mit der Verbindung tun, was Sie wollen. Nach allem, was wir wissen, könnte jeder Staffelläufer zwischen den Rennen in die Kneipe gehen. Eine Stärke der Ereignisabstraktion ist, dass Ihr Code nichts wissen oder sich darum kümmern muss.Einige Betriebssysteme enthalten Ereignishandhabungscode, der seinen Teil des Rennens ausführt, den Taktstock loslässt und dann zu seinem Startpunkt zurückkehrt, um auf den Start des nächsten Rennens zu warten. In diesem Sinne wird die Ereignisbehandlung für das Abrufen in vielen gleichzeitigen Schleifen optimiert. Es gibt jedoch immer einen externen Trigger, der den Prozess auslöst. Der Ereignis-Listener ist keine Funktion, die nicht zurückgegeben wird, sondern eine Funktion, die auf diesen externen Trigger wartet, bevor er ausgeführt wird. Eher, als:
Ich betrachte dies als:
und zwischen dem
signal
und dem nächsten Mal, wenn der Handler ausgeführt wird, wird konzeptionell kein Code ausgeführt oder eine Schleife ausgeführt.quelle
forever: { select(); do stuff; }
, treten Sie jedes Mal über die Schleife wieder in das Rennen ein. Egal, ob Sie dies wiederholt aus einem einzelnen Thread oder parallel auf separaten Threads oder Prozessoren tun, ich stelle mir jedes Ereignis als eine eigene Rasse vor. Ein Webbrowser ist beispielsweise ein Multithread-Programm mit mehreren Ereignisschleifen in separaten Threads, mindestens eine für die Benutzeroberfläche und eine für jede heruntergeladene Seite. Die Frage, die ich beim Codieren stelle, lautet: "Wie kann ich Ereignisse schnell genug verarbeiten?" Manchmal ist die Antwort eine Schleife, manchmal Fäden, oft eine Kombination.Nein, es handelt sich nicht um "optimiertes Polling". Eine Ereignisschleife verwendet eine Interrupt-gesteuerte E / A, anstatt eine Abfrage durchzuführen.
While-, Until-, For- usw.-Schleifen sind Polling-Schleifen.
"Polling" ist der Vorgang, bei dem wiederholt etwas überprüft wird. Da die Codeschleife führt kontinuierlich, und weil es eine kleine, „dicht“ Schleife, gibt es wenig Zeit für den Prozessor Aufgaben zu wechseln und etwas anderes tun. Fast alle "Hänge", "Einfrieren", "Abstürze" oder wie auch immer Sie es nennen möchten, wenn der Computer nicht mehr reagiert, sind die Manifestation von Code, der in einer unbeabsichtigten Abfrageschleife steckt. Die Instrumentierung zeigt eine 100% ige CPU-Auslastung an.
Interruptgesteuerte Ereignisschleifen sind weitaus effizienter als Abfrageschleifen. Polling ist eine äußerst verschwenderische Nutzung von CPU-Zyklen, daher werden alle Anstrengungen unternommen, um diese zu eliminieren oder zu minimieren.
Um die Codequalität zu optimieren, versuchen die meisten Sprachen jedoch, das Polling-Schleifen-Paradigma für Event-Handing-Befehle so genau wie möglich zu verwenden, da sie in einem Programm funktional ähnlichen Zwecken dienen. Da das Abrufen die bekanntere Methode ist, auf einen Tastendruck oder etwas anderes zu warten, ist es für den Unerfahrenen einfach, es zu verwenden und ein Programm zu starten, das möglicherweise von selbst einwandfrei läuft, während es ausgeführt wird, funktioniert jedoch nichts anderes. Es hat die Maschine "übernommen".
Wie in anderen Antworten erläutert, wird beim Interrupt-gesteuerten Event-Handing im Wesentlichen ein "Flag" in der CPU gesetzt und der Prozess wird "angehalten" (darf nicht ausgeführt werden), bis dieses Flag von einem anderen Prozess (wie der Tastatur) geändert wird Fahrer, der es ändert, wenn der Benutzer eine Taste gedrückt hat). Wenn das Flag ein tatsächlicher Hardware-Zustand ist, wie beispielsweise eine Leitung, die "hochgezogen" ist, wird es "Interrupt" oder "Hardware-Interrupt" genannt. Die meisten sind jedoch nur als Speicheradresse in der CPU oder im Hauptspeicher (RAM) implementiert und werden als "Semaphore" bezeichnet.
Semaphore können softwaregesteuert geändert werden und bieten so einen sehr schnellen und einfachen Signalisierungsmechanismus zwischen Softwareprozessen.
Interrupts können jedoch nur durch Hardware geändert werden. Am häufigsten werden Interrupts verwendet, die in regelmäßigen Abständen vom internen Clock-Chip ausgelöst werden. Eine der unzähligen Arten von Softwareaktionen, die durch Taktunterbrechungen aktiviert werden, ist das Ändern von Semaphoren.
Ich habe viel ausgelassen, musste aber irgendwo aufhören. Bitte fragen Sie, ob Sie weitere Informationen benötigen.
quelle
In der Regel ist die Antwort Hardware, das Betriebssystem und die Hintergrundthreads, die Sie nicht kontrollieren, verschwören sich, damit es mühelos aussieht. Die Netzwerkkarte empfängt einige Daten und löst einen Interrupt aus , um die CPU zu informieren. Der Interrupt-Handler des Betriebssystems erledigt dies. Dann wird ein Hintergrund-Thread, den Sie nicht kontrollieren (der durch die Registrierung für das Ereignis erstellt wurde und der seit Ihrer Registrierung für das Ereignis nicht mehr aktiv ist), vom Betriebssystem als Teil der Ereignisbehandlung aufgeweckt und führt Ihren Ereignishandler aus.
quelle
Ich gehe gegen alle anderen Antworten, die ich bisher sehe, und sage "Ja" . Ich denke, die anderen Antworten erschweren die Dinge zu sehr. Aus konzeptioneller Sicht sind alle Ereignisschleifen im Wesentlichen:
Wenn Sie zum ersten Mal versuchen, Ereignisschleifen zu verstehen, schadet es nicht, wenn Sie sie als einfache Schleife betrachten. Einige zugrunde liegende Frameworks warten auf die Übermittlung eines Ereignisses durch das Betriebssystem, leiten das Ereignis dann an einen oder mehrere Handler weiter und warten dann auf das nächste Ereignis usw. Aus Sicht der Anwendungssoftware ist das wirklich alles.
quelle
Nicht alle Ereignisauslöser werden in Schleifen behandelt. So schreibe ich oft meine eigenen Event-Engines:
Beachten Sie, dass sich Memo 1 zwar in einer Schleife befindet, die Schleife jedoch zur Benachrichtigung der einzelnen Listener dient. Der Ereignisauslöser selbst befindet sich nicht unbedingt in einer Schleife.
Theoretisch können Schlüsselereignisse auf Betriebssystemebene dieselbe Technik verwenden (obwohl ich denke, dass sie häufig stattdessen abfragen? Ich spekuliere hier nur), sofern das Betriebssystem eine Art
registerListener
API bereitstellt.quelle