Skip to content

Blocking vs Non-Blocking I/O

Blocking I/O

Thread waits doing nothing until the operation completes.

Thread: ---- read() ---- (blocked, waiting) ---- data ready ---- continues
  • Simple to write.
  • Thread is wasted while waiting.
  • Bad for high concurrency — need one thread per connection.

Non-Blocking I/O

Thread doesn't wait — operation returns immediately, you check later.

Thread: -- read() -- "not ready yet" -- do other work -- check again -- data ready
  • More complex.
  • Thread can do other work while waiting.
  • Basis of high-concurrency servers.

The Problem Non-Blocking Solves

Blocking (10k connections):

Thread 1     -- waiting for client 1 --------------------------
Thread 2     -- waiting for client 2 --------------------------
...
Thread 10000 -- waiting ---------------------------------------
(10k threads = huge memory, context switching overhead)

Non-Blocking (10k connections):

Thread 1 -- checks all 10k sockets, handles whoever is ready --
(1 thread handles everything)

I/O Models (Important for Interviews)

ModelHow
Blocking I/OWait until done
Non-blocking I/OReturn immediately, poll manually
I/O Multiplexingselect() / epoll() — kernel tells you who's ready
Async I/OKernel does I/O, notifies you when complete (callback)

select() vs epoll()

  • select() — checks all fds every time, O(n), limit of 1024 fds.
  • epoll() — kernel tracks which fds are ready, O(1), no fd limit.
  • Used by Nginx, Node.js under the hood.
epoll flow:
epoll_create() → epoll_ctl(add fds) → epoll_wait() → returns only ready fds
Single Thread

    ├── epoll watching 10k sockets

    ├── Socket 5 is ready  → run callback
    ├── Socket 99 is ready → run callback
    └── Socket 7 is ready  → run callback

(Event Loop = non-blocking I/O + callbacks)

How Node.js Uses This

Node.js is single-threaded but handles massive concurrency because it never blocks — always async I/O via libuv.

Sync vs Async vs Blocking vs Non-Blocking

BlockingNon-Blocking
SyncWait, do nothingPoll manually until ready
Async(doesn't exist)Kernel notifies via callback

Most people say async = non-blocking but technically async means kernel handles it and calls you back.