Lesson 4 — Concurrency with Threads & Channels
🎯 Learning Objectives
- Hiểu cách tạo và quản lý thread trong Rust.
- Biết dùng
mpsc::channelđể giao tiếp giữa các thread.
✅ Explanation & Key Concepts
std::thread::spawn tạo một thread mới để chạy closure. Nếu closure mượn dữ liệu, cần dùng move để chuyển ownership.
std::sync::mpsc (multi-producer, single-consumer) cho phép gửi message từ nhiều thread đến một receiver duy nhất.
Ownership + borrowing đảm bảo dữ liệu được chia sẻ giữa các thread an toàn, compiler ngăn data race.
💻 Example Implementation
Cargo.toml
[package]
name = "lesson04_concurrency"
version = "0.1.0"
edition = "2024"
src/main.rs
use std::sync::mpsc; use std::thread; use std::time::Duration; fn main() { let (tx, rx) = mpsc::channel(); // Spawn a worker thread let handle = thread::spawn(move || { for i in 1..=5 { let msg = format!("Worker says: Hi, dp — {}", i); tx.send(msg).unwrap(); thread::sleep(Duration::from_millis(500)); } }); // Main thread receives messages // take(5): only receives the first 5 messages from the channel then stops for received in rx.iter().take(5) { println!("Main got: {received}"); } // join(): ensures the child thread finished before the main thread exits handle.join().unwrap(); }
🛠️ Hands-on Exercises
- Basic: Tạo 2 thread, mỗi thread gửi 5 message về main. In tất cả message ra màn hình.
- Intermediate: Viết hàm
spawn_logger(rx: Receiver<String>)chạy trong thread riêng, chỉ nhận và in log. Main gửi log qua channel. - Challenge: Xây worker pool 4 thread xử lý số nguyên từ 1..20, mỗi worker tính bình phương rồi gửi kết quả về main để in.
🐞 Common Pitfalls & Debugging Tips
- Quên
movekhi spawn closure → borrow checker báo lỗi. - Dùng channel bị drop sớm → sender/receiver closed, gây panic khi
send. - Block main thread vô hạn khi không
joinhoặc không giới hạn số message.
❓ Q&A — Common Questions
Q1. Rust đảm bảo an toàn bộ nhớ khi multi-thread bằng cách nào?
- Nhờ hệ thống ownership/borrowing + trait
Send/Sync. Chỉ các kiểu an toàn mới được chia sẻ/transferred giữa threads. Borrow checker ngăn data race ở compile-time: không cho vừa mutable-borrow vừa shared-borrow trùng thời gian.
Q2. move closure trong thread::spawn dùng để làm gì?
movechuyển ownership (hoặc copy/clone nếu phù hợp) các biến môi trường vào closure chạy trên thread mới. Điều này tránh việc thread mới mượn dữ liệu đã hết thời (dropped) ở thread cũ.
Q3. Khi nào dùng channel (mpsc) thay vì Arc<Mutex<T>)?
- Channel: phù hợp mô hình message passing (không chia sẻ trạng thái), pipeline/queue, nhiều producer một consumer. Tránh lock-contention, tách biệt luồng dữ liệu.
Arc<Mutex<T>: phù hợp khi nhiều thread cần truy cập/chỉnh cùng một cấu trúc dữ liệu dùng chung. Đơn giản nhưng có chi phí lock và dễ deadlock nếu thiết kế kém. Gợi ý: ưu tiên message passing trước; chỉ dùng shared-state + lock khi thực sự cần.
🔄 Migration Notes (Rust 2024+)
- Concurrency core (
thread,mpsc) vẫn ổn định nhiều năm. - Edition 2024 cải thiện ergonomics khi capture biến vào closure với
move. - Với task nặng I/O, xem xét async (
tokio) nhưng thread/channel luôn là nền tảng.
📚 References
📖 Glossary of Terms
- Thread: luồng thực thi độc lập.
- Channel (mpsc): cơ chế gửi nhận message giữa thread.
- move closure: chuyển ownership vào thread.
- Join handle: giá trị trả về từ
thread::spawn, dùng đểjoin().
🌿 Wisdom Note
Vạn vật sinh ư hữu, hữu sinh ư vô.
All things are born from being. Being is born from non-being.
Concurrency cũng vậy: từ một main thread (một “hữu”), ta sinh ra nhiều worker thread (vạn vật), cùng hợp tác nhưng không phá vỡ an toàn bộ nhớ.