How one server serves thousands of players at once. Threads and the race conditions they cause, the synchronization primitives that make shared state safe, why blocking I/O does not scale (the C10k problem), event loops and I/O multiplexing (epoll/kqueue/io_uring), async/await and coroutines that make async code readable, and the actor model for scaling out.
Before you start
Comfort with code is enough — examples are in Python (the Python course). The Network Programming course provides the servers this one makes scale. Concepts transfer to Go, C++, C#, and Rust.
Threads & the Server
A game server must handle many players at once. Threads let code run concurrently — but sharing data between them introduces race conditions, the defining hazard of concurrent programming.
Synchronization Primitives
To share data safely across threads you coordinate access with primitives — mutexes, semaphores, condition variables. Learn what each does, and the deadlocks that misusing them causes.
Blocking vs Async I/O
The thread-per-connection model hits a wall at scale — the C10k problem. Understand blocking I/O, why it wastes threads waiting, and how non-blocking I/O lets one thread serve thousands of connections.
Event Loops & I/O Multiplexing
How does one thread watch thousands of sockets? The OS tells it which are ready via select, poll, epoll, kqueue, or io_uring. Learn I/O multiplexing and the reactor pattern behind every async server.
Async/Await & Coroutines
Event-loop code with raw callbacks becomes tangled. Async/await and coroutines let you write asynchronous code that reads sequentially while still running on a single-threaded event loop.
The Actor Model & Scaling Out
To scale beyond one machine and avoid shared-state bugs, model the server as independent actors that own their state and communicate by messages. Then shard the world across many servers.