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 (FastAPIdef, 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
- Coroutine — unit suspend/resume: cơ chế khiến non-block + async sống được trên ít thread.
- Concurrency vs parallel — deal vs do.
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.