Suche…


Einführung

In diesem Beitrag werden wir diskutieren, wie das Konzept von Eventloop entstand und wie es für Hochleistungsserver und ereignisgesteuerte Anwendungen wie GUIs verwendet werden kann.

Wie sich das Konzept der Ereignisschleife entwickelt hat.

Eventloop im Pseudocode

Eine Ereignisschleife ist eine Schleife, die auf Ereignisse wartet und dann auf diese Ereignisse reagiert

while true:
    wait for something to happen
    react to whatever happened

Beispiel für einen Single-Thread-HTTP-Server ohne Ereignisschleife

    while true:
    socket = wait for the next TCP connection
    read the HTTP request headers from (socket)
    file_contents = fetch the requested file from disk
    write the HTTP response headers to (socket)
    write the (file_contents) to (socket)
    close(socket)

Hier ist eine einfache Form eines HTTP-Servers, der ein einzelner Thread ist, jedoch keine Ereignisschleife. Das Problem hier ist, dass es wartet, bis jede Anforderung abgeschlossen ist, bevor mit der Verarbeitung der nächsten begonnen wird. Wenn es eine Weile dauert, um die HTTP-Anforderungsheader zu lesen oder die Datei von der Festplatte abzurufen, sollten wir in der Lage sein, die nächste Anforderung zu verarbeiten, während wir warten, bis diese abgeschlossen ist.

Die gebräuchlichste Lösung besteht darin, das Programm multithreadig zu machen.

Beispiel für einen Multithread-HTTP-Server ohne Ereignisschleife

function handle_connection(socket):
    read the HTTP request headers from (socket)
    file_contents = fetch the requested file from disk
    write the HTTP response headers to (socket)
    write the (file_contents) to (socket)
    close(socket)
while true:
    socket = wait for the next TCP connection
    spawn a new thread doing handle_connection(socket)

Jetzt haben wir unseren kleinen HTTP-Server mit mehreren Threads versehen. Auf diese Weise können wir sofort zur nächsten Anforderung übergehen, da die aktuelle Anforderung in einem Hintergrundthread ausgeführt wird. Viele Server, einschließlich Apache, verwenden diesen Ansatz.

Aber es ist nicht perfekt. Eine Einschränkung ist, dass Sie nur so viele Threads erzeugen können. Bei Workloads, bei denen Sie über eine große Anzahl von Verbindungen verfügen, aber jede Verbindung nur gelegentlich Aufmerksamkeit erfordert, ist das Multithread-Modell nicht besonders leistungsfähig. Die Lösung für diese Fälle ist die Verwendung einer Ereignisschleife:

Beispiel eines HTTP-Servers mit Ereignisschleife

while true:
    event = wait for the next event to happen
    if (event.type == NEW_TCP_CONNECTION):
        conn = new Connection
        conn.socket = event.socket
        start reading HTTP request headers from (conn.socket) with userdata = (conn)
    else if (event.type == FINISHED_READING_FROM_SOCKET):
        conn = event.userdata
        start fetching the requested file from disk with userdata = (conn)
    else if (event.type == FINISHED_READING_FROM_DISK):
        conn = event.userdata
        conn.file_contents = the data we fetched from disk
        conn.current_state = "writing headers"
        start writing the HTTP response headers to (conn.socket) with userdata = (conn)
    else if (event.type == FINISHED_WRITING_TO_SOCKET):
        conn = event.userdata
        if (conn.current_state == "writing headers"):
            conn.current_state = "writing file contents"
            start writing (conn.file_contents) to (conn.socket) with userdata = (conn)
        else if (conn.current_state == "writing file contents"):
            close(conn.socket)

Hoffentlich ist dieser Pseudocode verständlich. Hier ist was los ist: Wir warten darauf, dass etwas passiert. Immer wenn eine neue Verbindung erstellt wird oder eine bestehende Verbindung unsere Aufmerksamkeit erfordert, gehen wir darauf ein und warten dann wieder. Auf diese Weise arbeiten wir gut, wenn es viele Verbindungen gibt und jede nur selten Aufmerksamkeit erfordert.

In einer echten Anwendung (nicht Pseudocode), die unter Linux ausgeführt wird, wird der Teil "Warten auf das nächste Ereignis" durch Aufrufen des Systemaufrufs poll () oder epoll () implementiert. Die Teile "Lesen / Schreiben von etwas in einen Socket" werden durch Aufruf der Systemaufrufe recv () oder send () im nicht blockierenden Modus implementiert.

Referenz:

[1]. "Wie funktioniert eine Ereignisschleife?" [Online]. Verfügbar: https://www.quora.com/How-does-an-event-loop-work



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow