Abstract
- An event loop is a scheduler used to achieve O. It manages I/O events and, once they are ready, dispatches the corresponding tasks (callbacks, coroutines, futures). While waiting for I/O, it can switch to and execute other tasks that are ready, ensuring efficient concurrency without blocking
Responsive
Suitable for Process (进程) that need to respond to events, such as a graphical user interface (GUI) program.
Event Loop OS-level Flow
- Event loop asks the kernel to start an I/O operation (e.g., “read from this socket” or “write to this file”). This is a non-blocking syscall (e.g.,
epoll_ctl
,kqueue
,IOCP
) - The kernel gives back an I/O handle / file descriptor that can be monitored for readiness (not data itself yet).
- The kernel tracks the I/O request internally, basically babysits the device driver queue
- Instead of the thread “periodically checking,” the thread calls a multiplexing syscall (like
epoll_wait
,select
,kqueue
, IOCP). This blocks the thread efficiently until any registered event becomes ready. So no busy-polling, it’s “sleep until kernel wakes me.” - When something happens (e.g., socket readable, file write complete), the kernel notifies the event loop and posts the event into the syscall’s result set. On some OSes this is via interrupts/upcalls at the driver level, which bubble up to
epoll
/kqueue
- The event loop thread collects these ready events and dispatches the corresponding callbacks, coroutines, or futures until completion
Asynchronous Single Threading
- Basically captures many of the benefits of Multi-threading without most of the risks of Race Condition (竞态条件) & Deadlock (死锁), because only one thread executes the event loop
No Parallelism
There is only one single Thread, so can’t take advantage of Multi-core Chip. A CPU Bounded task can block the execution of the entire program, thus no Parallelism (并行性)
Python Event Loop
- We run our async entrypoint inside an event loop with
asyncio.run(coro)
. On Linux, asyncio’s default loop isSelectorEventLoop
, which is mostly Python code usingepoll
via theselectors
module (with C-accelerated Tasks/Futures in CPython) uvloop
is a drop-in replacement written in Cython on top of libuv, so it has much lower overhead for scheduling and I/O, and is typically faster for I/O-bound workloads. uvloop makes asyncio 2-4x faster. Guide on integrating into asyncio. It is used by sglang which is a fast serving framework for large language models and vision language models
Gevent
Mixing gevent and asyncio is painful because they’re separate event-loop runtimes. Each wants to own the main thread’s scheduling, sockets/FD registrations, timers, and signal handlers. Gevent’s monkey-patching collides with asyncio’s expectations, causing missed wakeups, stalls, or deadlocks (not just “overhead”). For new code, asyncio + uvloop is the modern, fast, and well-supported path (structured concurrency, rich ecosystem, great perf for I/O).
If you must combine: isolate via separate processes (best) or a dedicated thread without
monkey.patch_all()
, and communicate over IPC.