事件循环的概念是如何演变的
在伪代码中的 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