目录
Rust 多线程基础
Rust 的多线程编程建立在标准库的 std::thread 模块之上。与其他语言不同,Rust 通过其所有权系统和类型系统来保证线程安全,避免了常见的并发问题如数据竞争。
核心概念
- 线程:操作系统级别的执行单元
- 所有权:Rust 的核心概念,确保每个值只有一个所有者
- 借用:临时访问他人拥有的值
- 同步原语 :如
Mutex、Arc等,用于线程间协调 - 通道:用于线程间安全通信
同步线程编程
基本线程创建
使用 thread::spawn 函数创建新线程:
rust
use std::thread;
use std::time::Duration;
fn main() {
println!("主线程开始");
// 创建新线程
let handle = thread::spawn(|| {
for i in 1..=5 {
println!("子线程: {}", i);
thread::sleep(Duration::from_millis(100));
}
});
// 主线程继续执行
for i in 1..=3 {
println!("主线程: {}", i);
thread::sleep(Duration::from_millis(150));
}
// 等待子线程完成
handle.join().unwrap();
println!("主线程结束");
}
运行结果:
主线程开始
主线程: 1
子线程: 1
子线程: 2
主线程: 2
子线程: 3
子线程: 4
主线程: 3
子线程: 5
主线程结束
线程间通信
使用通道(channel)实现线程间通信:
rust
use std::thread;
use std::sync::mpsc;
use std::time::Duration;
fn main() {
// 创建通道
let (tx, rx) = mpsc::channel();
// 发送线程
thread::spawn(move || {
let messages = ["Hello", "from", "the", "spawned", "thread"];
for msg in messages {
tx.send(msg).unwrap();
thread::sleep(Duration::from_millis(200));
}
});
// 接收线程(主线程)
println!("等待接收消息...");
for received in rx {
println!("接收到: {}", received);
}
println!("所有消息接收完毕");
}
运行结果:
等待接收消息...
接收到: Hello
接收到: from
接收到: the
接收到: spawned
接收到: thread
所有消息接收完毕
共享状态
使用 Arc 和 Mutex 实现线程安全的共享状态:
rust
use std::thread;
use std::sync::{Arc, Mutex};
use std::time::Duration;
fn main() {
// 创建共享计数器
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
// 创建 5 个线程
for i in 0..5 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..10 {
let mut num = counter.lock().unwrap();
*num += 1;
println!("线程 {}: 计数器 = {}", i, *num);
thread::sleep(Duration::from_millis(50));
}
});
handles.push(handle);
}
// 等待所有线程完成
for handle in handles {
handle.join().unwrap();
}
println!("最终计数器值: {}", *counter.lock().unwrap());
}
运行结果:
线程 0: 计数器 = 1
线程 0: 计数器 = 2
...
线程 4: 计数器 = 50
最终计数器值: 50
线程返回值
从线程中返回计算结果:
rust
use std::thread;
fn main() {
let handle = thread::spawn(|| {
println!("子线程开始计算");
let mut sum = 0;
for i in 1..=100 {
sum += i;
}
println!("子线程计算完成");
sum // 返回值
});
println!("主线程等待结果...");
let result = handle.join().unwrap();
println!("1 到 100 的和: {}", result);
}
运行结果:
主线程等待结果...
子线程开始计算
子线程计算完成
1 到 100 的和: 5050
线程池
创建简单的线程池处理任务:
rust
use std::thread;
use std::time::Duration;
fn main() {
let jobs = vec![1, 2, 3, 4, 5];
let mut handles = vec![];
println!("开始处理任务...");
for job in jobs {
let handle = thread::spawn(move || {
println!("处理任务 {}", job);
thread::sleep(Duration::from_millis(300));
println!("任务 {} 完成", job);
job * 2 // 返回处理结果
});
handles.push(handle);
}
// 收集所有线程的结果
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
println!("所有任务完成。结果: {:?}", results);
}
运行结果:
开始处理任务...
处理任务 1
处理任务 2
处理任务 3
处理任务 4
处理任务 5
任务 1 完成
任务 2 完成
任务 3 完成
任务 4 完成
任务 5 完成
所有任务完成。结果: [2, 4, 6, 8, 10]
异步线程编程
Tokio 异步运行时
Rust 的异步编程需要一个异步运行时,最常用的是 Tokio:
添加依赖:
toml
[dependencies]
tokio = { version = "1.36.0", features = ["full"] }
异步任务
创建和管理异步任务:
rust
use tokio;
#[tokio::main]
async fn main() {
println!("异步主线程开始");
// 创建异步任务
let task1 = tokio::spawn(async {
println!("异步任务 1 开始");
tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
println!("异步任务 1 完成");
100
});
let task2 = tokio::spawn(async {
println!("异步任务 2 开始");
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
println!("异步任务 2 完成");
200
});
// 等待所有异步任务完成
let (result1, result2) = tokio::join!(task1, task2);
println!("任务结果: {}, {}", result1.unwrap(), result2.unwrap());
println!("异步主线程结束");
}
运行结果:
异步主线程开始
异步任务 1 开始
异步任务 2 开始
异步任务 2 完成
异步任务 1 完成
任务结果: 100, 200
异步主线程结束
异步通道
异步线程间的消息传递:
rust
use tokio;
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
// 创建异步通道
let (tx, mut rx) = mpsc::channel(32);
// 发送任务
tokio::spawn(async move {
for i in 1..=5 {
tx.send(i).await.unwrap();
println!("发送: {}", i);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
});
// 接收任务
println!("等待接收消息...");
while let Some(msg) = rx.recv().await {
println!("接收: {}", msg);
}
println!("所有消息接收完毕");
}
运行结果:
等待接收消息...
发送: 1
接收: 1
发送: 2
接收: 2
发送: 3
接收: 3
发送: 4
接收: 4
发送: 5
接收: 5
所有消息接收完毕
异步共享状态
使用 Tokio 的 Mutex 实现异步共享状态:
rust
use tokio;
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() {
// 创建异步共享计数器
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
// 创建 3 个异步任务
for i in 0..3 {
let counter = Arc::clone(&counter);
let handle = tokio::spawn(async move {
for _ in 0..5 {
let mut num = counter.lock().await;
*num += 1;
println!("异步任务 {}: 计数器 = {}", i, *num);
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
}
});
handles.push(handle);
}
// 等待所有异步任务完成
for handle in handles {
handle.await.unwrap();
}
println!("最终计数器值: {}", *counter.lock().await);
}
运行结果:
异步任务 0: 计数器 = 1
异步任务 1: 计数器 = 2
异步任务 2: 计数器 = 3
异步任务 0: 计数器 = 4
异步任务 1: 计数器 = 5
异步任务 2: 计数器 = 6
异步任务 0: 计数器 = 7
异步任务 1: 计数器 = 8
异步任务 2: 计数器 = 9
异步任务 0: 计数器 = 10
异步任务 1: 计数器 = 11
异步任务 2: 计数器 = 12
异步任务 0: 计数器 = 13
异步任务 1: 计数器 = 14
异步任务 2: 计数器 = 15
最终计数器值: 15
线程安全
所有权与借用
Rust 的所有权系统是线程安全的基础:
- 所有权:每个值只能有一个所有者
- 借用:可以有多个不可变借用或一个可变借用
- 生命周期:确保引用在所有者有效的期间内有效
同步原语
Rust 提供了多种同步原语:
- Mutex:互斥锁,确保同一时间只有一个线程可以访问数据
- RwLock:读写锁,允许多个读操作或一个写操作
- Arc:原子引用计数,用于在多个线程间共享所有权
- Condvar:条件变量,用于线程间的信号通知
- Barrier:屏障,用于同步多个线程的执行
Send 和 Sync trait
- Send:标记类型可以安全地在线程间转移所有权
- Sync:标记类型可以安全地在线程间共享引用
Rust 的大多数类型都自动实现了这些 trait,但有些类型(如 Rc、RefCell)没有实现,因为它们不是线程安全的。
性能优化
线程数量
- CPU 密集型任务:线程数通常设置为 CPU 核心数
- I/O 密集型任务:线程数可以大于 CPU 核心数
- 异步任务:可以创建大量轻量级的异步任务
避免竞争
- 减少锁的范围:只在必要时持有锁
- 使用无锁数据结构 :如
AtomicUsize - 使用通道:优先使用通道进行线程间通信,而不是共享状态
异步 vs 同步
- 同步线程:适合 CPU 密集型任务,每个线程对应一个操作系统线程
- 异步任务:适合 I/O 密集型任务,多个任务可以在同一个操作系统线程上运行
最佳实践
- 优先使用通道:通过消息传递进行线程间通信
- 最小化共享状态:减少线程间的依赖
- 使用 Arc<Mutex>:需要共享可变状态时的标准做法
- 异步编程:I/O 密集型任务优先使用异步
- 错误处理:妥善处理线程中的错误
- 资源管理:确保线程正确释放资源
完整代码示例
以下是一个完整的 Rust 多线程示例,包含了本文介绍的所有功能:
rust
use std::thread;
use std::time::Duration;
use std::sync::{mpsc, Arc, Mutex};
use tokio;
// 基本线程示例
struct ThreadData {
name: String,
}
impl ThreadData {
fn new(name: String) -> Self {
println!("ThreadData created with name: {}", name);
ThreadData { name }
}
fn thread_function(&self) {
for i in 1..=5 {
println!("[Basic Thread] thread name: {} spawned thread: {}", self.name, i);
thread::sleep(Duration::from_millis(100));
}
}
}
// 线程间通信示例
fn channel_example() {
println!("\n=== Channel Example ===");
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let messages = ["Hello", "from", "the", "spawned", "thread"];
for msg in messages {
tx.send(msg).unwrap();
thread::sleep(Duration::from_millis(200));
}
});
for received in rx {
println!("[Channel] Received: {}", received);
}
}
// 共享状态示例
fn shared_state_example() {
println!("\n=== Shared State Example ===");
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for i in 0..5 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..10 {
let mut num = counter.lock().unwrap();
*num += 1;
println!("[Shared State] Thread {}: Counter = {}", i, *num);
thread::sleep(Duration::from_millis(50));
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("[Shared State] Final counter value: {}", *counter.lock().unwrap());
}
// 线程返回值示例
fn thread_return_example() {
println!("\n=== Thread Return Example ===");
let handle = thread::spawn(move || {
let mut sum = 0;
for i in 1..=100 {
sum += i;
}
sum
});
let result = handle.join().unwrap();
println!("[Thread Return] Sum from 1 to 100: {}", result);
}
// 线程池示例
fn thread_pool_example() {
println!("\n=== Thread Pool Example ===");
let jobs = vec![1, 2, 3, 4, 5];
let mut handles = vec![];
for job in jobs {
let handle = thread::spawn(move || {
println!("[Thread Pool] Processing job {}", job);
thread::sleep(Duration::from_millis(300));
println!("[Thread Pool] Job {} completed", job);
job * 2
});
handles.push(handle);
}
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
println!("[Thread Pool] All jobs completed. Results: {:?}", results);
}
// 异步线程示例
async fn async_example() {
println!("\n=== Async Thread Example ===");
let task1 = tokio::spawn(async {
println!("[Async] Task 1 started");
tokio::time::sleep(tokio::time::Duration::from_millis(300)).await;
println!("[Async] Task 1 completed");
100
});
let task2 = tokio::spawn(async {
println!("[Async] Task 2 started");
tokio::time::sleep(tokio::time::Duration::from_millis(200)).await;
println!("[Async] Task 2 completed");
200
});
let (result1, result2) = tokio::join!(task1, task2);
println!("[Async] Task results: {}, {}", result1.unwrap(), result2.unwrap());
// 异步通道示例
println!("\n=== Async Channel Example ===");
let (tx, mut rx) = tokio::sync::mpsc::channel(32);
tokio::spawn(async move {
for i in 1..=5 {
tx.send(i).await.unwrap();
println!("[Async Channel] Sent: {}", i);
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
}
});
while let Some(msg) = rx.recv().await {
println!("[Async Channel] Received: {}", msg);
}
// 异步共享状态示例
println!("\n=== Async Shared State Example ===");
let counter = Arc::new(tokio::sync::Mutex::new(0));
let mut handles = vec![];
for i in 0..3 {
let counter = Arc::clone(&counter);
let handle = tokio::spawn(async move {
for _ in 0..5 {
let mut num = counter.lock().await;
*num += 1;
println!("[Async Shared] Task {}: Counter = {}", i, *num);
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
}
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
println!("[Async Shared] Final counter value: {}", *counter.lock().await);
}
fn main() {
println!("=== Rust Multi-threading Examples ===");
// 1. 基本线程示例
println!("\n=== Basic Thread Example ===");
let x = 10;
println!("Creating ThreadData instances...");
let thread_data1 = ThreadData::new("Thread 1".to_string());
let thread_data2 = ThreadData::new("Thread 2".to_string());
println!("Spawning threads...");
let handler = thread::spawn(move || {
thread_data1.thread_function();
});
let handler2 = thread::spawn(move || {
thread_data2.thread_function();
});
println!("x = {}", x);
handler.join().unwrap();
handler2.join().unwrap();
// 2. 线程间通信示例
channel_example();
// 3. 共享状态示例
shared_state_example();
// 4. 线程返回值示例
thread_return_example();
// 5. 线程池示例
thread_pool_example();
// 6. 异步线程示例
println!("\n=== Running Async Examples ===");
tokio::runtime::Builder::new_multi_thread()
.worker_threads(4)
.enable_all()
.build()
.unwrap()
.block_on(async_example());
println!("\n=== All Examples Complete! ===");
}
总结
Rust 提供了强大而安全的多线程编程支持,通过其所有权系统和类型安全特性,使得多线程编程更加可靠。本文介绍了 Rust 中的各种线程使用方法,包括:
- 同步线程:基本线程创建、线程间通信、共享状态、线程返回值和线程池
- 异步线程:基于 Tokio 的异步任务、异步通道和异步共享状态
- 线程安全:所有权系统、同步原语和 Send/Sync trait
- 性能优化:线程数量选择、避免竞争和异步编程
通过本文的学习,你应该能够掌握 Rust 多线程编程的核心概念和实践技巧,编写安全、高效的并发程序。
Rust 的多线程编程虽然有一定的学习曲线,但其带来的安全性和性能优势是值得的。随着经验的积累,你会发现 Rust 的多线程编程变得越来越自然和直观。
希望本文对你有所帮助,祝你在 Rust 多线程编程的道路上越走越远!