Rust 精要系列(七)—— 并发与多线程模型解析
Rust 的并发系统并非“让你更容易写出并发代码”,而是让错误的并发代码无法通过编译。借助所有权、生命周期与 trait 系统,Rust 构建了一套零成本抽象的并发模型,使得开发者能够编写高性能且安全的并行程序。在下一篇中,我们将继续深入探讨异步 I/O 与 Future 执行原理,进一步理解 Rust 如何在性能与安全之间取得平衡。
Rust 在设计之初就十分重视并发安全(Concurrency Safety),其核心目标是:在不牺牲性能的前提下,杜绝数据竞争(Data Race)。
相比于传统语言中容易出现的线程共享与同步问题,Rust 通过所有权系统与类型检查,在编译阶段就能保证并发代码的正确性。
本文将从基础概念入手,深入解析 Rust 的并发模型、常见的并发工具、以及实际应用示例。
一、并发与并行的区别
Rust 官方文档中强调区分两个重要概念:
-
并发(Concurrency):多个任务在同一时间段交替执行,例如 I/O 操作与计算任务交替。
-
并行(Parallelism):多个任务真正同时运行,通常需要多核 CPU 支持。
Rust 的并发模型涵盖了这两者:
通过 std::thread 模块提供线程级并行;
通过异步 async/await 模型提供任务级并发。
二、线程基础:std::thread
Rust 提供了轻量级线程创建方式:
use std::thread;
use std::time::Duration;
fn main() {
let handle = thread::spawn(|| {
for i in 1..5 {
println!("子线程输出:{}", i);
thread::sleep(Duration::from_millis(100));
}
});
for i in 1..5 {
println!("主线程输出:{}", i);
thread::sleep(Duration::from_millis(50));
}
handle.join().unwrap();
}
解析:
-
thread::spawn用于创建新线程。 -
join()用于等待子线程执行结束。 -
Rust 会强制要求线程间共享数据时必须是安全的。
三、线程间通信:通道(Channel)
Rust 借鉴了 Go 的设计,引入了**消息传递(Message Passing)**模型。
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let message = String::from("Hello from thread!");
tx.send(message).unwrap();
});
let received = rx.recv().unwrap();
println!("主线程接收到:{}", received);
}
解析:
-
mpsc::channel()表示 multi-producer, single-consumer,即多发送者单接收者模型。 -
send()与recv()用于跨线程传递所有权安全的数据。 -
Rust 编译器确保发送的数据类型实现了
Sendtrait,从而避免潜在的数据竞争。
四、共享状态与互斥锁
Rust 不提倡共享状态,但当确实需要时,提供了安全的同步原语:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..5 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("最终计数结果:{}", *counter.lock().unwrap());
}
解析:
-
Arc是多线程安全的引用计数指针(Atomic Reference Counted)。 -
Mutex提供互斥访问机制。 -
Rust 的类型系统保证只有当持有锁的线程释放后,其他线程才能访问数据。
五、异步并发模型(Async/Await)
Rust 的异步模型基于 Future Trait 与 任务调度器(Executor)。
异步任务不会创建真实的操作系统线程,而是通过事件循环(Event Loop)来轮询执行。
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let t1 = async_task("任务1");
let t2 = async_task("任务2");
futures::join!(t1, t2);
}
async fn async_task(name: &str) {
for i in 1..=3 {
println!("{} - 步骤 {}", name, i);
sleep(Duration::from_millis(100)).await;
}
}
解析:
-
使用
tokio运行时执行异步任务。 -
join!可并行等待多个任务。 -
异步任务间不会产生竞争,因为它们共享的是不可变状态,且执行时机由调度器严格控制。
六、Send 与 Sync Trait
Rust 的并发安全核心在于两个特殊 trait:
-
Send:允许类型在不同线程间传递所有权。
-
Sync:允许类型的引用在多个线程中安全共享。
例如:
-
Arc<T>实现了Send与Sync。 -
Rc<T>仅在单线程中安全(不实现Send)。
编译器会根据这些 trait 自动检测线程安全性,防止出现数据竞争。
七、总结
Rust 的并发系统并非“让你更容易写出并发代码”,而是让错误的并发代码无法通过编译。
借助所有权、生命周期与 trait 系统,Rust 构建了一套零成本抽象的并发模型,使得开发者能够编写高性能且安全的并行程序。
在下一篇中,我们将继续深入探讨 异步 I/O 与 Future 执行原理,进一步理解 Rust 如何在性能与安全之间取得平衡。
更多推荐

所有评论(0)