Bài concurrency vs parallel nói:

Concurrency = dealing with lots of things at once
Parallelism = doing lots of things at once

Blocking / non-blocking trả lời câu hẹp hơn, khi bạn đang chờ I/O (hay một lời gọi lâu):

  • Blocking — unit đang chạy (thread, worker, cả event loop nếu gọi sync) đứng đó đến khi xong. Trong lúc chờ, unit đó không deal việc khác.
  • Non-blocking — gọi xong, nếu chưa sẵn sàng thì trả quyền ngay; việc được ghi “đang chờ event”. Unit đó vẫn deal request/task khác, quay lại khi data về (callback, await, completion queue…).

Nói ngắn: blocking = chờ mà kẹt deal; non-blocking = chờ mà vẫn deal được.

Demo timeline: 1 thread block vs non-block.

Blocking — deal một việc, xong mới việc sau

buf = read(fd)          // thread ngủ đến khi có data
row = db.query(...)     // đứng chờ DB
body = http.get(url)    // stack không đi tiếp

Dễ đọc, dễ suy luận. Giá: mỗi “chỗ đứng chờ” chiếm một thread (hoặc đóng băng loop). Muốn dealing with 1000 request đang chờ DB theo kiểu blocking thuần → thường cần ~1000 thread (hoặc chấp nhận hàng đợi dài).

Trên Node/asyncio: một API sync block trong handler = cả loop ngừng deal mọi request khác — concurrent trên giấy, serial trên thực tế.

Non-blocking — chờ mà vẫn dealing with

// fd non-block / async
data = await read_async(fd)
// task A suspend — loop/thread đi deal B, C
// data về → resume A

Tầng dưới hay là non-blocking fd + epoll/kqueue/IOCP, hoặc thread pool ẩn. Điểm quan trọng không phải cú pháp async — mà: trong lúc A chờ, hệ thống còn deal B được không?

  • Có → non-blocking (theo nghĩa thực dụng) → concurrency I/O sống được trên ít thread.
  • Không → blocking, dù function tên async_* nhưng bên trong ôm thread đến khi xong.

Gắn vào dealing / doing

Dealing with (concurrency)
  cần non-blocking (hoặc nhiều thread block song song)
  → nhiều việc “mở” cùng lúc mà không chết throughput

Doing (parallel)
  non-blocking không tự tạo thêm core
  → CPU nặng vẫn cần nhiều worker/process/pod

Blocking + N thread
  vừa deal (mỗi thread một việc) vừa có thể do (N core)
  → đắt stack, context switch; pool size = trần in-flight

Nhà bếp (cùng tinh thần bài concurrency):

  • Block: đứng nhìn nồi sôi — không deal món khác.
  • Non-block: hẹn giờ, đi deal việc khác — vẫn 1 đầu bếp (chưa parallel).
  • Parallel: thêm đầu bếp — doing cùng lúc.

Async API ≠ non-blocking

async / Promise / Future = cách viết code suspend được. Non-blocking = khi chờ, unit không bị giữ.

  • await run_in_executor(blocking_db) — API async, bên trong block một thread pool (FastAPI def, một số driver). Deal được trên loop chính; pool vẫn có trần blocking.
  • Busy-poll non-block fd trong vòng lặp — fd non-block nhưng CPU spin: không “deal” khéo, chỉ đốt core.

Hỏi một câu: chờ xong, ai đã làm việc khác hộ?

Block I/O vs block CPU

  • Block I/O: thread ngủ — core có thể chạy thread khác (doing vẫn có thể ở chỗ khác trong process/máy).
  • Block CPU trên event loop: tight loop — không ai được deal; non-block socket cũng không cứu. Cần offload hoặc parallel doing.

Thực dụng

  • I/O-bound + muốn dealing with nhiều connection: non-block / async (Node, asyncio, Go netpoll, Netty…).
  • Sync stack (Tomcat, Gunicorn sync): mỗi request một thread — trần pool = trần deal đồng thời; quá pool → xếp hàng / 503.
  • Pool size = ngầm “max blocking in-flight” — nối unbounded, backpressure.

Đọc tiếp

Một câu để nhớ

Blocking = chờ mà kẹt, không deal việc khác.
Non-blocking = chờ mà vẫn dealing with được.

Non-blocking giúp concurrency (deal). Nó không thay parallelism (do) khi bài toán là CPU. Chọn theo chỗ đang chết: chờ I/O thì đừng block unit; nặng tính thì thêm unit để doing.