Coroutine /
await nhường runner.
Event loop chỉ chạy tiếp
việc khác khi đoạn hiện tại
chịu nhường. Đó là
cooperative scheduling.
OS thread thì khác:
preemptive — scheduler có thể cắt giữa chừng
(time slice), task không cần gọi await.
Hai mô hình
Cooperative
· Task tự yield (await, yield, channel recv…)
· Giữa hai yield: ôm CPU/stack một mình
· Starvation nếu ai đó không yield
Preemptive
· OS/runtime cắt theo policy
· Fair hơn với CPU-bound lẫn lộn
· Context switch đắt hơn; shared memory race dễ hơn
Hệ quả production
- JS/Node: tight loop / JSON lớn / crypto sync = không yield = kẹt cả loop (mọi await ready cũng đứng).
- Cancel cooperative: không check signal = không dừng.
- Thread pool preemptive: một job CPU không chặn thread khác trên core khác — nhưng shared lock vẫn block.
- Go: goroutine trông cooperative; runtime có preemption tại safepoint — “lai”.
Starvation — ví dụ
// Ôm loop: không await → không ai khác chạy
while (Date.now() < end) { crunch() }
// “Hơi” cooperative nhưng microtask đói timer
function spin() {
Promise.resolve().then(spin) // không nhường macrotask
}
spin()
Xem event loop — microtask drain và sync block.
Khi nào cooperative đủ
I/O-bound, code hay await, đoạn sync ngắn. Đúng chỗ
dealing with
trên ít thread.
Khi cần preemption / offload
CPU dài, thư viện sync, không tin mọi path sẽ yield → offload, process riêng, parallel doing.
Một câu
Cooperative chỉ công bằng khi mọi người thật sự nhường.
Preempt cắt giúp fairness CPU — đổi lấy chi phí và race.