Rust provides safe concurrency through its type system. Rust's "fearless concurrency" slogan means the compiler catches data races and thread-safety issues at compile time rather than letting them surface at runtime.
Rust cung cấp lập trình đồng thời an toàn thông qua hệ thống kiểu của mình. Slogan 'fearless concurrency' (đồng thời không sợ hãi) của Rust có nghĩa là compiler sẽ bắt lỗi data race và các vấn đề an toàn luồng tại compile time thay vì để chúng xảy ra lúc chạy chương trình.
The thread::spawn function creates a new thread and takes a closure to execute. It returns a JoinHandle you can use to wait for the thread to finish. If you do not call join(), the main thread may exit before the spawned thread completes.
Hàm thread::spawn tạo một luồng mới và nhận một closure để thực thi. Hàm trả về một JoinHandle mà bạn có thể dùng để chờ luồng hoàn thành. Nếu không gọi join(), luồng chính có thể kết thúc trước khi luồng con hoàn thành.
1use std::thread;2use std::time::Duration;34fn main() {5 // Spawn a new thread6 let handle = thread::spawn(|| {7 for i in 1..=5 {8 println!("spawned thread: {}", i);9 thread::sleep(Duration::from_millis(1));10 }11 });12By default, closures borrow values from their surrounding environment. But since a spawned thread might outlive the thread that created it, Rust requires you to move data into the thread rather than borrow it. The move keyword forces the closure to take ownership of all captured values.
Theo mặc định, closures mượn các giá trị từ môi trường xung quanh. Nhưng vì luồng mới có thể tồn tại lâu hơn luồng tạo ra nó, Rust yêu cầu bạn di chuyển (move) dữ liệu vào luồng thay vì mượn. Từ khóa move buộc closure lấy quyền sở hữu tất cả các giá trị nó sử dụng.
1use std::thread;23fn main() {4 let data = vec![1, 2, 3];56 // ERROR: cannot borrow data here because the thread7 // might outlive the current function8 // let handle = thread::spawn(|| println!("{:?}", data));910 // CORRECT: move transfers ownership into the thread11 let handle = thread::spawn(move || {12 println!("Thread has: {:?}", data);Rust follows the philosophy of "communicate by sending messages rather than sharing memory" for inter-thread communication. The mpsc::channel() function creates a channel with one sender and one receiver. mpsc stands for "multiple producer, single consumer."
Rust theo phương châm 'truyền thông điệp thay vì chia sẻ bộ nhớ' cho giao tiếp giữa các luồng. Hàm mpsc::channel() tạo một kênh với một sender và một receiver. mpsc là viết tắt của 'multiple producer, single consumer' (nhiều người gửi, một người nhận).
1use std::sync::mpsc;2use std::thread;3use std::time::Duration;45fn main() {6 // tx = transmitter (sender), rx = receiver7 let (tx, rx) = mpsc::channel();89 thread::spawn(move || {10 let messages = vec!["hello", "from", "the", "thread"];11 for msg in messages {12 tx.send(msg).unwrap();The "multiple producer" part of mpsc means you can have multiple senders sending to the same receiver. You use tx.clone() to create additional senders. All clones of the sender transmit to the same receiver.
Phần 'multiple producer' trong mpsc có nghĩa là bạn có thể có nhiều sender gửi đến cùng một receiver. Bạn dùng tx.clone() để tạo thêm các sender. Tất cả các bản sao của sender đều gửi đến cùng một receiver.
1use std::sync::mpsc;2use std::thread;3use std::time::Duration;45fn main() {6 let (tx, rx) = mpsc::channel();78 // Clone sender for each producer thread9 let tx1 = tx.clone();10 let tx2 = tx.clone();1112 thread::spawn(move || {A Mutex (Mutual Exclusion) ensures that only one thread can access the data at a time. To access the data, a thread must request the lock. When done, the lock is automatically released when the MutexGuard goes out of scope.
Mutex (Mutual Exclusion) đảm bảo rằng chỉ có một luồng có thể truy cập dữ liệu tại một thời điểm. Để truy cập dữ liệu, một luồng phải yêu cầu khóa (lock). Sau khi xử lý xong, khóa được tự động giải phóng khi MutexGuard ra khỏi phạm vi.
1use std::sync::Mutex;23fn main() {4 let m = Mutex::new(5);56 {7 // lock() blocks until the lock is acquired8 // Returns a MutexGuard (smart pointer to the data)9 let mut num = m.lock().unwrap();10 *num = 6;11 // Lock released here when MutexGuard drops12 }To share a Mutex across multiple threads, you need to wrap it in Arc<T>. Arc provides thread-safe multiple ownership, while Mutex provides safe mutable access. This combination is the most common pattern for shared mutable state across threads.
Để chia sẻ Mutex giữa nhiều luồng, bạn cần bọc nó trong Arc<T>. Arc cung cấp nhiều chủ sở hữu an toàn đa luồng, còn Mutex cung cấp quyền truy cập đột biến an toàn. Đây là mẫu kết hợp phổ biến nhất cho trạng thái chia sẻ có thể thay đổi giữa các luồng.
1use std::sync::{Arc, Mutex};2use std::thread;34// Example: accumulate results from worker threads5fn parallel_sum(numbers: Vec<i32>) -> i32 {6 let result = Arc::new(Mutex::new(0));7 let mut handles = vec![];8 let chunk_size = numbers.len() / 4;910 for chunk in numbers.chunks(chunk_size) {11 let result = Arc::clone(&result);12 let chunk = chunk.to_vec();Arc<Mutex<T>> là bộ đôi đa luồng của Rc<RefCell<T>>. Dùng Rc<RefCell<T>> cho đơn luồng và Arc<Mutex<T>> cho đa luồng.
Rust has two special marker traits for concurrency safety. Send means ownership of that type can be transferred between threads. Sync means references to that type can be shared between threads. Most primitive types are both Send and Sync.
Rust có hai marker trait đặc biệt để đảm bảo an toàn đồng thời. Send nghĩa là quyền sở hữu của kiểu đó có thể được chuyển giao giữa các luồng. Sync nghĩa là tham chiếu đến kiểu đó có thể được chia sẻ giữa các luồng. Hầu hết các kiểu nguyên thủy đều là Send và Sync.
1use std::sync::{Arc, Mutex};23// Send: ownership can be moved to another thread4// Most types are automatically Send5// Exceptions: Rc<T>, raw pointers, RefCell<T> in cross-thread context67// Sync: &T is safe to send to another thread8// A type T is Sync if &T is Send9// Mutex<T> is Sync (controlled access)10// Cell<T> and RefCell<T> are NOT Sync1112// Example: a custom type that is Send + SyncA deadlock occurs when two threads are each waiting for the other to release a lock, causing both to block forever. Rust does not automatically prevent deadlocks, but there are several strategies to avoid them.
Deadlock xảy ra khi hai luồng đang chờ nhau giải phóng khóa, dẫn đến cả hai bị chặn vĩnh viễn. Rust không tự động ngăn deadlock, nhưng có một số chiến lược để tránh chúng.
1use std::sync::{Arc, Mutex};2use std::thread;34// DEADLOCK scenario (avoid this pattern):5// Thread 1: locks A, then tries to lock B6// Thread 2: locks B, then tries to lock A78// STRATEGY 1: Always acquire locks in the same order9fn safe_transfer(10 account_a: &Arc<Mutex<f64>>,11 account_b: &Arc<Mutex<f64>>,12 amount: f64,Rayon is a third-party crate providing easy data parallelism. By replacing regular iterators with parallel iterators, you can leverage all CPU cores without manual thread management. Rayon automatically splits work among threads in a thread pool.
Rayon là một crate bên thứ ba cung cấp song song dữ liệu dễ sử dụng. Bằng cách thay thế các iterator thông thường bằng iterator song song, bạn có thể tận dụng tất cả các lõi CPU mà không cần quản lý luồng thủ công. Rayon tự động chia công việc giữa các luồng trong thread pool.
1// Cargo.toml:2// [dependencies]3// rayon = "1"45use rayon::prelude::*;67fn main() {8 let numbers: Vec<i32> = (1..=1_000_000).collect();910 // Sequential sum11 let seq_sum: i32 = numbers.iter().sum();12Rayon không phải lúc nào cũng nhanh hơn code tuần tự. Với các tập dữ liệu nhỏ hoặc các phép toán rất nhanh, chi phí tạo luồng có thể lớn hơn lợi ích song song hóa.
Key Takeaways
Điểm Chính
- Rust prevents data races at compile timeRust ngăn chặn data race tại thời điểm biên dịch
- Use channels (mpsc) for message-passing between threadsSử dụng channel (mpsc) để truyền thông điệp giữa các luồng
- Mutex provides safe shared mutable state with lockingMutex cung cấp trạng thái chia sẻ có thể thay đổi an toàn với khóa
- Arc enables thread-safe reference counting for shared ownershipArc cho phép đếm tham chiếu an toàn giữa các luồng
Practice
Test your understanding of this chapter
What does 'mpsc' stand for in std::sync::mpsc?
"mpsc" trong std::sync::mpsc là viết tắt của gì?
Why is Rc<T> not allowed to be sent across thread boundaries while Arc<T> is?
Tại sao Rc<T> không được phép gửi qua ranh giới luồng trong khi Arc<T> thì được?
Rust's type system prevents deadlocks at compile time, just as it prevents data races.
Hệ thống kiểu của Rust ngăn chặn deadlock tại compile time, giống như cách nó ngăn chặn data race.
When using a move closure with thread::spawn, all captured variables are moved into the closure and are no longer accessible in the original thread.
Khi sử dụng move closure với thread::spawn, tất cả biến được nắm bắt đều bị di chuyển vào closure và không còn truy cập được trong luồng gốc.
Acquire a Mutex lock to safely modify shared data across threads
Lấy khóa Mutex để sửa đổi an toàn dữ liệu chia sẻ giữa các luồng
let counter = Arc::new(Mutex::new(0)); let clone = Arc::clone(&counter); thread::spawn(move || { let mut num = clone.().unwrap(); *num += 1; });