Một câu đủ dùng (Rob Pike / Go talks — ý được nhắc lại rất nhiều nơi):

Concurrency is about dealing with lots of things at once.
Parallelism is about doing lots of things at once.
  • Dealing with — bạn đang gánh nhiều việc cùng một giai đoạn: mở dở, chờ, xen kẽ, xếp hàng, không bỏ rơi. Chưa chắc hai việc đang chạy instruction cùng một nanosecond.
  • Doing — nhiều việc thật sự được thực thi cùng lúc: nhiều core, nhiều máy, nhiều worker vật lý.

Tiếng Việt gần: concurrency = xoay xở nhiều việc một lúc; parallel = làm nhiều việc một lúc.

Demo animation (1 core xoay xở vs 2 core cùng làm): concurrency vs parallel.

Dealing with — concurrent

Nhân viên support 1 người, 20 ticket mở: vừa reply A, vừa chờ khách B, vừa đọc log C. Cùng lúc đang deal nhiều ticket — không phải 20 người gõ phím cùng lúc.

Trong code: event loop, async/await, goroutine chờ I/O, connection pool đầy request dang dở…

// Đang “deal” 100 fetch — phần lớn thời gian là chờ mạng
await Promise.all(urls.map((u) => fetch(u)))
// 1 thread JS: dealing with lots of I/O
// không phải 100 core đang parse body cùng lúc

Concurrency là cách tổ chức để hệ thống chịu được nhiều việc in-flight: ai chờ cái gì, khi nào resume, không block hết cả nhà vì một lời gọi chậm. Thường đi với non-blocking.

Có thể concurrent trên một core: OS/runtime xen kẽ. Bạn vẫn “deal with lots of things” — chỉ là lần lượt trong từng lát thời gian rất ngắn, hoặc chồng chờ I/O.

Doing — parallel

Hai (hay N) người support cùng lúc, mỗi người gõ một ticket — doing nhiều việc at once trên nhiều “bộ não”.

Trong code: multi-thread/process CPU, shard job trên nhiều pod, GPU hàng nghìn lane…

// N process cùng compress — doing trên N core (nếu có)
await processPool.map(chunks, compress)

Parallel cần nhiều đơn vị thực thi cùng lúc. Thêm replica K8s, bật worker pool, chia data / task — đều là tăng “doing”.

Cùng lúc trên giấy vs cùng lúc trên silicon

Dealing with (concurrent)
  timeline logic: A, B, C đều “đang mở”
  silicon: có thể chỉ 1 core; A chờ DB thì B chạy

Doing (parallel)
  timeline logic: A và B đều “đang compute”
  silicon: 2 core cùng fetch/decode instruction

Người ta hay nhầm vì cả hai đều nói “at once”. Khác ở at once kiểu nào: nhiều việc trong tay (deal) vs nhiều việc đang chạy (do).

Một hình số

3 request × (CPU 10ms + I/O 100ms + CPU 10ms)

Chỉ dealing, 1 core, non-block:
  3 request “mở” chồng I/O → wall ~ 100 + 60 ms
  dealing with 3; doing CPU vẫn 1 luồng

Doing thêm 2 core (+ vẫn deal I/O):
  hai đoạn CPU có thể cháy cùng wall-clock
  dealing + doing — wall ngắn hơn nữa

Khi nào cần cái nào

  • Nhiều chờ (DB, HTTP, disk): cần dealing with giỏi — concurrent + non-block — kẻo một thread/process chỉ gánh được ít request.
  • Nhiều tính (hash, encode, aggregate): cần doing trên nhiều core — parallel; chỉ async không rút wall CPU.
  • Hệ thật: thường cả hai — nhiều instance doing song song, mỗi instance dealing with hàng nghìn connection.

Runtime (một dòng mỗi cái)

  • Node / asyncio: mạnh dealing with I/O; doing CPU → worker/process khác.
  • Go: goroutine = dealing with rẻ; scheduler map lên nhiều OS thread = doing khi có multi-core + work.
  • K8s N pod: doing ở mức máy; trong mỗi pod vẫn phải dealing with request.

Đọc tiếp

Một câu để nhớ

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

Deal = nhiều việc dang dở, có tổ chức. Do = nhiều việc đang chạy thật trên nhiều unit. Nhầm “at once” là tối ưu sai chỗ: bọc async cho bài CPU, hoặc thêm core cho chỗ chỉ cần biết chờ cho khéo.