Lesson 03 — Thread & Concurrency

Thread vs Process, Concurrency vs Parallelism, và Race Condition.

1. Thread chia sẻ Address Space — nhưng Stack riêng

Sơ đồ dưới minh hoạ một Process với 2 Thread. Phần màu xanh/hồng là riêng của từng thread; phần nền là chia sẻ.

Process (PID=1234) — 1 Address Space Text Segment (Code) — CHIA SẺ cùng machine code, cùng binary — 1 bản duy nhất trong RAM Heap & Data — CHIA SẺ counter=0 (tại 0x602010) — cả hai thread đều thấy, đều ghi được Thread 1 Stack — RIÊNG rsp = 0x7fff_9000 PC = 0x400A80 local_x = 42 return_addr = 0x400B00 Thread 2 Stack — RIÊNG rsp = 0x7ffe_8000 PC = 0x400C20 local_y = 99 return_addr = 0x400D10 File Descriptor Table — CHIA SẺ fd[0]=stdin fd[1]=stdout fd[2]=stderr fd[3]=log.txt Cả 2 thread cùng thấy → ghi đồng thời vào fd[3] cần lock! 🔵 Thread 1: process_request() 🔴 Thread 2: do_computation()
Kết luận: Thread nhẹ hơn process vì chỉ cần tạo stack mới (~8 KB – 8 MB), không cần sao chép toàn bộ address space. Giao tiếp qua biến global/heap trực tiếp — không cần IPC như pipe hay socket. Nhưng: chia sẻ là con dao hai lưỡi — một thread viết sai địa chỉ có thể corrupt dữ liệu thread khác.

2. Concurrency vs Parallelism — Minh họa Timeline

Chọn kịch bản để xem timeline thực thi của Thread A và Thread B.

3. Mô phỏng Race Condition — counter++

Hai thread cùng tăng counter++ 10 lần mỗi thread. Kết quả mong đợi: 20. Nhưng với race condition, kết quả có thể khác.

Thread A
for i in 0..10:
  rax = counter ← đọc
  rax += 1 ← tăng
  counter = rax ← ghi
Thread B
for i in 0..10:
  rax = counter ← đọc
  rax += 1 ← tăng
  counter = rax ← ghi
60%
0
Mong đợi: 20 | Thực tế:
Nhấn ▶ Chạy để mô phỏng...

4. Mô hình Threading: 1:1 vs M:N

1:1 (Kernel Thread)
User Thread ↔ Kernel Thread
U1 — U2 — U3
↕      ↕      ↕
K1 — K2 — K3
↕      ↕      ↕
Core1 Core2 Core1
  • Stack: 8 MB/thread
  • Tạo: ~20 μs (syscall)
  • Switch: ~1-10 μs (kernel)
  • Max: ~32,768 thread
  • Ví dụ: pthreads, Java thread
M:N (Go Goroutine)
M goroutine → N kernel thread
G1 G2 G3 G4 G5 G6
   ↕      ↕      ↕
   K1     K2     K3
   ↕      ↕      ↕
Core1 Core2 Core3
  • Stack: 2 KB khởi đầu
  • Tạo: ~1 μs (user space)
  • Switch: ~100 ns (runtime)
  • Max: hàng triệu goroutine
  • Ví dụ: Go, Erlang, Java Loom
Xu hướng hiện đại: M:N (goroutine, virtual thread) — tạo 100,000 goroutine ≈ 200 MB RAM thay vì 800 GB (1:1 thread)