
本篇作为进入并发编程之前的地基,讲清楚两个关键自动 trait:Send 和 Sync,以及在多线程下如何安全地共享和修改数据。包含 Arc、Mutex、RwLock、消息通道(channel)与常见陷阱。
Send:类型的所有权可以在线程间转移(move)。绝大多数标准类型都是 Send。Sync:如果 T: Sync,则 &T 可以在多个线程间共享(即 &T 是 Send)。Send 关乎“能否把值交给另一个线程”;Sync 关乎“能否把不可变引用同时给多个线程用”。常见:
i32, String, Vec<T>:Send。&T:当且仅当 T: Sync 时,&T: Send。Rc<T>:非线程安全(不是 Send/Sync);Arc<T>:线程安全引用计数。Cell<T>/RefCell<T>:非 Sync,仅单线程;Mutex<T>/RwLock<T>:Sync,用于并发。use std::sync::Arc;
use std::thread;
fn main() {
let data = Arc::new(vec![1,2,3]);
let mut handles = vec![];
for _ in 0..4 {
let shared = Arc::clone(&data); // 增加引用计数
handles.push(thread::spawn(move || {
// 只读共享:无需加锁
shared.iter().sum::<i32>()
}));
}
let total: i32 = handles.into_iter().map(|h| h.join().unwrap()).sum();
println!("sum = {}", total);
}
结论:只读共享用 Arc<T> 即可;若需写,则配合锁。
Mutex<T>:互斥锁,独占写;RwLock<T>:读写锁,多读一写;Arc 搭配才能跨线程共享所有权。use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0u64));
let mut handles = vec![];
for _ in 0..8 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || {
for _ in 0..100_000 {
*c.lock().unwrap() += 1; // 临界区
}
}));
}
for h in handles { h.join().unwrap(); }
println!("count = {}", *counter.lock().unwrap());
}
要点:
lock() 返回 MutexGuard<T>,实现了 DerefMut,出作用域自动解锁。RwLock 示例:
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let store = Arc::new(RwLock::new(Vec::<u32>::new()));
// 写入线程
{
let s = Arc::clone(&store);
std::thread::spawn(move || {
for i in 0..10 { s.write().unwrap().push(i); }
});
}
// 读取线程
{
let s = Arc::clone(&store);
std::thread::spawn(move || {
loop {
let snapshot = s.read().unwrap().clone(); // 短暂持锁,尽快释放
if snapshot.len() >= 10 { break; }
}
});
}
}相比共享可变状态,“消息传递”往往更简单更安全:
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel::<String>();
thread::spawn(move || {
for i in 0..3 {
tx.send(format!("msg-{i}")).unwrap();
thread::sleep(Duration::from_millis(50));
}
});
for received in rx { println!("got: {}", received); }
}
mpsc:多生产者、单消费者。broadcast 模式(如 tokio::sync)。Rc<T> 与 RefCell<T> 并非线程安全:不实现 Send/Sync。Arc<T> 与 Mutex<T>/RwLock<T>。Rc<RefCell<T>> 仍然非常好用,但请清晰限定单线程语境。Arc<Mutex<T>> 共享可变数据;mpsc 通知与任务派发;use std::sync::{Arc, Mutex, mpsc};
use std::thread;
fn main() {
let state = Arc::new(Mutex::new(0usize));
let (tx, rx) = mpsc::channel::<usize>();
// worker
{
let st = Arc::clone(&state);
thread::spawn(move || {
for job in rx { *st.lock().unwrap() += job; }
});
}
// dispatcher
for n in [1,2,3,4,5] { tx.send(n).unwrap(); }
drop(tx); // 关闭通道,结束 worker 循环
// 等待片刻或 join 线程(示意)
std::thread::sleep(std::time::Duration::from_millis(10));
println!("final = {}", *state.lock().unwrap());
}
Arc<Mutex<T>> 或 Arc<RwLock<T>>。Arc<T> 读多写少)。tokio::sync::Mutex)。Arc<Mutex<Vec<u32>>> 在多个线程累加 0..10_000,验证最终长度与内容。mpsc 发送 100 条任务,worker 统计字节总数,最后主线程打印总和。Rc<RefCell<T>> 迁移到多线程版本,改成 Arc<Mutex<T>> 并跑通。至此,并发的核心安全基石已搭好。后续可进入线程池、通道模式、异步运行时(Tokio)、并发数据结构的系统实践。