事件迴圈的概念是如何演變的

在虛擬碼中的 Eventloop

事件迴圈是一個等待事件然後對這些事件作出反應的迴圈

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

沒有事件迴圈的單執行緒 HTTP 伺服器的示例

    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)

這是一個簡單形式的 HTTP 伺服器,它是一個單執行緒但沒有事件迴圈。這裡的問題是它等待每個請求完成後再開始處理下一個請求。如果需要一段時間來讀取 HTTP 請求標頭或從磁碟獲取檔案,我們應該能夠在等待完成時開始處理下一個請求。

最常見的解決方案是使程式多執行緒化。

沒有事件迴圈的多執行緒 HTTP 伺服器的示例

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)

現在我們已經使我們的小 HTTP 伺服器多執行緒。這樣,我們可以立即轉到下一個請求,因為當前請求正在後臺執行緒中執行。包括 Apache 在內的許多伺服器都使用這種方法。

但它並不完美。一個限制是你只能產生這麼多執行緒。對於具有大量連線的工作負載,但每個連線只需要偶爾注意一次,多執行緒模型將無法很好地執行。這些情況的解決方案是使用事件迴圈:

具有事件迴圈的 HTTP 伺服器的示例

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)

希望這個虛擬碼是可理解的。這是正在發生的事情:我們等待事情發生。無論何時建立新連線或現有連線需要我們注意,我們都會處理它,然後再回去等待。這樣,當有很多連線時我們表現很好,每個連線都很少需要注意。

在 Linux 上執行的實際應用程式(非虛擬碼)中,將通過呼叫 poll() 或 epoll()系統呼叫來實現等待下一個事件發生部分。通過在非阻塞模式下呼叫 recv() 或 send()系統呼叫來實現“開始讀取/寫入套接字”部分。

參考:

[1]。 “事件迴圈如何運作?” [線上]。可用: https//www.quora.com/How-does-an-event-loop-work