Rust 多线程与并发编程详解
Rust 的并发模型基于"无畏并发"(Fearless Concurrency)理念,通过所有权系统在编译期防止数据竞争。
目录
线程基础
1. 创建线程
rust
use std::thread;
use std::time::Duration;
fn main() {
// 创建新线程
let handle = thread::spawn(|| {
for i in 1..10 {
println!("子线程: {}", i);
thread::sleep(Duration::from_millis(1));
}
});
// 主线程
for i in 1..5 {
println!("主线程: {}", i);
thread::sleep(Duration::from_millis(1));
}
// 等待子线程完成
handle.join().unwrap();
}
2. 带返回值的线程
rust
use std::thread;
fn main() {
let handle = thread::spawn(|| {
// 线程执行计算
let result = (1..=100).sum::<i32>();
result // 返回值
});
// 获取线程返回值
let result = handle.join().unwrap();
println!("计算结果: {}", result);
}
3. 捕获外部变量(move 闭包)
rust
use std::thread;
fn main() {
let v = vec![1, 2, 3];
// 使用 move 将所有权转移到线程
let handle = thread::spawn(move || {
println!("向量: {:?}", v);
});
handle.join().unwrap();
// v 已被移动,不能再使用
}
4. 线程命名和构建器
css
use std::thread;
fn main() {
let builder = thread::Builder::new()
.name("worker-thread".to_string())
.stack_size(4 * 1024 * 1024); // 4MB 栈
let handle = builder.spawn(|| {
println!("线程名: {:?}", thread::current().name());
// 执行任务
}).unwrap();
handle.join().unwrap();
}
消息传递
Rust 使用 通道(Channel) 实现线程间通信,遵循"不要通过共享内存通信,而要通过通信来共享内存"。
1. 单生产者单消费者(mpsc)
rust
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
// 创建通道
let (tx, rx) = mpsc::channel();
// 发送者线程
thread::spawn(move || {
let messages = vec![
"消息1",
"消息2",
"消息3",
];
for msg in messages {
tx.send(msg).unwrap();
thread::sleep(Duration::from_millis(100));
}
});
// 接收者(主线程)
for received in rx {
println!("收到: {}", received);
}
}
2. 多生产者单消费者
rust
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
// 克隆发送端,创建多个生产者
for i in 0..3 {
let tx_clone = tx.clone();
thread::spawn(move || {
let msg = format!("线程 {} 的消息", i);
tx_clone.send(msg).unwrap();
});
}
// 显式丢弃原始发送端
drop(tx);
// 接收所有消息
for received in rx {
println!("收到: {}", received);
}
}
3. 有界通道(同步通道)
rust
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
// 创建容量为 2 的有界通道
let (tx, rx) = mpsc::sync_channel(2);
thread::spawn(move || {
for i in 1..=5 {
println!("发送: {}", i);
tx.send(i).unwrap(); // 队列满时会阻塞
println!("已发送: {}", i);
}
});
thread::sleep(Duration::from_secs(2));
for received in rx {
println!("收到: {}", received);
thread::sleep(Duration::from_millis(500));
}
}
4. 通道选择(Crossbeam)
rust
use crossbeam::channel;
use std::thread;
use std::time::Duration;
fn main() {
let (tx1, rx1) = channel::unbounded();
let (tx2, rx2) = channel::unbounded();
// 生产者1
thread::spawn(move || {
thread::sleep(Duration::from_millis(100));
tx1.send("来自通道1").unwrap();
});
// 生产者2
thread::spawn(move || {
thread::sleep(Duration::from_millis(200));
tx2.send("来自通道2").unwrap();
});
// 选择先到达的消息
channel::select! {
recv(rx1) -> msg => println!("收到: {:?}", msg),
recv(rx2) -> msg => println!("收到: {:?}", msg),
}
}
共享状态并发
1. 互斥锁(Mutex)
rust
use std::sync::Mutex;
use std::thread;
fn main() {
let counter = Mutex::new(0);
let mut handles = vec![];
for _ in 0..10 {
let handle = thread::spawn(move || {
// 这里有问题:counter 被移动了
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("结果: {}", *counter.lock().unwrap());
}
上面的代码会报错,需要使用 Arc:
2. Arc + Mutex(正确的方式)
rust
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
// Arc: 原子引用计数,允许多个所有者
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
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());
}
3. RwLock(读写锁)
rust
use std::sync::{Arc, RwLock};
use std::thread;
fn main() {
let data = Arc::new(RwLock::new(vec![1, 2, 3]));
let mut handles = vec![];
// 多个读者
for i in 0..5 {
let data = Arc::clone(&data);
let handle = thread::spawn(move || {
let r = data.read().unwrap();
println!("读者 {}: {:?}", i, *r);
});
handles.push(handle);
}
// 一个写者
let data_write = Arc::clone(&data);
let writer = thread::spawn(move || {
let mut w = data_write.write().unwrap();
w.push(4);
println!("写者添加了 4");
});
handles.push(writer);
for handle in handles {
handle.join().unwrap();
}
println!("最终数据: {:?}", *data.read().unwrap());
}
4. 原子类型(Atomic)
rust
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::thread;
fn main() {
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
for _ in 0..1000 {
counter.fetch_add(1, Ordering::SeqCst);
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("结果: {}", counter.load(Ordering::SeqCst));
}
5. 条件变量(Condvar)
rust
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::Duration;
fn main() {
let pair = Arc::new((Mutex::new(false), Condvar::new()));
let pair_clone = Arc::clone(&pair);
// 等待线程
thread::spawn(move || {
let (lock, cvar) = &*pair_clone;
let mut started = lock.lock().unwrap();
while !*started {
println!("等待信号...");
started = cvar.wait(started).unwrap();
}
println!("收到信号,开始工作!");
});
thread::sleep(Duration::from_secs(1));
// 通知线程
let (lock, cvar) = &*pair;
let mut started = lock.lock().unwrap();
*started = true;
cvar.notify_one();
println!("已发送信号");
}
异步编程
Rust 的异步编程基于 async/await 语法和 Future trait。
1. 基本 async/await
rust
use tokio;
#[tokio::main]
async fn main() {
let result = async_function().await;
println!("结果: {}", result);
}
async fn async_function() -> i32 {
println!("开始异步操作...");
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
println!("异步操作完成");
42
}
2. 并发执行多个异步任务
rust
use tokio;
#[tokio::main]
async fn main() {
let task1 = tokio::spawn(async {
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
println!("任务1完成");
1
});
let task2 = tokio::spawn(async {
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
println!("任务2完成");
2
});
// 等待所有任务完成
let (result1, result2) = tokio::join!(task1, task2);
println!("结果: {:?}, {:?}", result1, result2);
}
3. select! 宏(竞争执行)
rust
use tokio;
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let task1 = async {
sleep(Duration::from_secs(1)).await;
"任务1"
};
let task2 = async {
sleep(Duration::from_secs(2)).await;
"任务2"
};
// 哪个先完成就返回哪个
tokio::select! {
result = task1 => println!("先完成: {}", result),
result = task2 => println!("先完成: {}", result),
}
}
4. 异步通道
rust
use tokio::sync::mpsc;
#[tokio::main]
async fn main() {
let (tx, mut rx) = mpsc::channel(100);
// 发送者任务
tokio::spawn(async move {
for i in 1..=5 {
tx.send(i).await.unwrap();
println!("发送: {}", i);
}
});
// 接收者
while let Some(i) = rx.recv().await {
println!("收到: {}", i);
}
}
5. 异步互斥锁
rust
use tokio::sync::Mutex;
use std::sync::Arc;
#[tokio::main]
async fn main() {
let data = Arc::new(Mutex::new(0));
let mut handles = vec![];
for i in 0..10 {
let data = Arc::clone(&data);
let handle = tokio::spawn(async move {
let mut lock = data.lock().await;
*lock += 1;
println!("任务 {} 增加计数", i);
});
handles.push(handle);
}
for handle in handles {
handle.await.unwrap();
}
println!("最终计数: {}", *data.lock().await);
}
并发模式与最佳实践
1. 线程池模式
rust
use std::sync::{Arc, Mutex, mpsc};
use std::thread;
type Job = Box<dyn FnOnce() + Send + 'static>;
struct ThreadPool {
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
impl ThreadPool {
fn new(size: usize) -> ThreadPool {
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender }
}
fn execute<F>(&self, f: F)
where
F: FnOnce() + Send + 'static,
{
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
struct Worker {
id: usize,
thread: thread::JoinHandle<()>,
}
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
let thread = thread::spawn(move || loop {
let job = receiver.lock().unwrap().recv();
match job {
Ok(job) => {
println!("Worker {} 执行任务", id);
job();
}
Err(_) => {
println!("Worker {} 断开连接", id);
break;
}
}
});
Worker { id, thread }
}
}
fn main() {
let pool = ThreadPool::new(4);
for i in 0..8 {
pool.execute(move || {
println!("任务 {} 执行", i);
thread::sleep(std::time::Duration::from_secs(1));
});
}
thread::sleep(std::time::Duration::from_secs(10));
}
2. Actor 模式
rust
use tokio::sync::mpsc;
struct Actor {
receiver: mpsc::Receiver<ActorMessage>,
state: i32,
}
enum ActorMessage {
Increment,
Decrement,
GetValue(tokio::sync::oneshot::Sender<i32>),
}
impl Actor {
fn new(receiver: mpsc::Receiver<ActorMessage>) -> Self {
Actor {
receiver,
state: 0,
}
}
async fn run(mut self) {
while let Some(msg) = self.receiver.recv().await {
match msg {
ActorMessage::Increment => {
self.state += 1;
println!("增加,当前值: {}", self.state);
}
ActorMessage::Decrement => {
self.state -= 1;
println!("减少,当前值: {}", self.state);
}
ActorMessage::GetValue(respond_to) => {
let _ = respond_to.send(self.state);
}
}
}
}
}
#[derive(Clone)]
struct ActorHandle {
sender: mpsc::Sender<ActorMessage>,
}
impl ActorHandle {
fn new() -> Self {
let (sender, receiver) = mpsc::channel(8);
let actor = Actor::new(receiver);
tokio::spawn(actor.run());
ActorHandle { sender }
}
async fn increment(&self) {
let _ = self.sender.send(ActorMessage::Increment).await;
}
async fn decrement(&self) {
let _ = self.sender.send(ActorMessage::Decrement).await;
}
async fn get_value(&self) -> i32 {
let (send, recv) = tokio::sync::oneshot::channel();
let _ = self.sender.send(ActorMessage::GetValue(send)).await;
recv.await.unwrap()
}
}
#[tokio::main]
async fn main() {
let actor = ActorHandle::new();
actor.increment().await;
actor.increment().await;
actor.decrement().await;
let value = actor.get_value().await;
println!("最终值: {}", value);
}
3. 生产者-消费者模式
rust
use std::sync::{Arc, Mutex};
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::sync_channel(5); // 缓冲区大小为 5
// 生产者
let producer = thread::spawn(move || {
for i in 1..=10 {
println!("生产: {}", i);
tx.send(i).unwrap();
thread::sleep(Duration::from_millis(100));
}
});
// 消费者
let consumer = thread::spawn(move || {
for item in rx {
println!("消费: {}", item);
thread::sleep(Duration::from_millis(300));
}
});
producer.join().unwrap();
consumer.join().unwrap();
}
4. 屏障同步(Barrier)
rust
use std::sync::{Arc, Barrier};
use std::thread;
fn main() {
let mut handles = Vec::with_capacity(5);
let barrier = Arc::new(Barrier::new(5));
for i in 0..5 {
let c = Arc::clone(&barrier);
handles.push(thread::spawn(move || {
println!("线程 {} 准备中...", i);
thread::sleep(std::time::Duration::from_millis(i * 100));
// 等待所有线程到达屏障
c.wait();
println!("线程 {} 开始执行", i);
}));
}
for handle in handles {
handle.join().unwrap();
}
}
实战示例
示例 1:并发下载器
rust
use std::sync::{Arc, Mutex};
use tokio;
struct DownloadManager {
completed: Arc<Mutex<Vec<String>>>,
}
impl DownloadManager {
fn new() -> Self {
Self {
completed: Arc::new(Mutex::new(Vec::new())),
}
}
async fn download(&self, url: String) -> Result<(), Box<dyn std::error::Error>> {
println!("开始下载: {}", url);
// 模拟下载
tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
// 记录完成
let mut completed = self.completed.lock().unwrap();
completed.push(url.clone());
println!("下载完成: {}", url);
Ok(())
}
async fn download_all(&self, urls: Vec<String>) {
let mut tasks = vec![];
for url in urls {
let manager = self.clone();
let task = tokio::spawn(async move {
manager.download(url).await
});
tasks.push(task);
}
for task in tasks {
let _ = task.await;
}
}
fn get_completed(&self) -> Vec<String> {
self.completed.lock().unwrap().clone()
}
}
impl Clone for DownloadManager {
fn clone(&self) -> Self {
Self {
completed: Arc::clone(&self.completed),
}
}
}
#[tokio::main]
async fn main() {
let manager = DownloadManager::new();
let urls = vec![
"https://example.com/file1.zip".to_string(),
"https://example.com/file2.zip".to_string(),
"https://example.com/file3.zip".to_string(),
];
manager.download_all(urls).await;
println!("\n已完成下载:");
for url in manager.get_completed() {
println!(" - {}", url);
}
}
示例 2:并发 Web 爬虫
rust
use tokio::sync::{Semaphore, Mutex};
use std::sync::Arc;
use std::collections::HashSet;
struct WebCrawler {
visited: Arc<Mutex<HashSet<String>>>,
semaphore: Arc<Semaphore>,
}
impl WebCrawler {
fn new(max_concurrent: usize) -> Self {
Self {
visited: Arc::new(Mutex::new(HashSet::new())),
semaphore: Arc::new(Semaphore::new(max_concurrent)),
}
}
async fn crawl(&self, url: String) {
// 检查是否已访问
{
let mut visited = self.visited.lock().await;
if visited.contains(&url) {
return;
}
visited.insert(url.clone());
}
// 获取许可(限制并发数)
let _permit = self.semaphore.acquire().await.unwrap();
println!("爬取: {}", url);
// 模拟网络请求
tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
println!("完成: {}", url);
}
async fn crawl_all(&self, urls: Vec<String>) {
let mut tasks = vec![];
for url in urls {
let crawler = self.clone();
let task = tokio::spawn(async move {
crawler.crawl(url).await;
});
tasks.push(task);
}
for task in tasks {
let _ = task.await;
}
}
async fn visited_count(&self) -> usize {
self.visited.lock().await.len()
}
}
impl Clone for WebCrawler {
fn clone(&self) -> Self {
Self {
visited: Arc::clone(&self.visited),
semaphore: Arc::clone(&self.semaphore),
}
}
}
#[tokio::main]
async fn main() {
let crawler = WebCrawler::new(3); // 最多3个并发
let urls = (1..=10)
.map(|i| format!("https://example.com/page{}", i))
.collect();
crawler.crawl_all(urls).await;
println!("\n总共爬取了 {} 个页面", crawler.visited_count().await);
}
示例 3:本项目实际应用(数据库连接池)
rust
// 这是本项目中 Tokio 异步编程的实际应用
use sqlx::MySqlPool;
use tokio;
// 数据库操作(异步)
async fn create_user(pool: &MySqlPool, username: &str, email: &str) -> Result<i64, sqlx::Error> {
let result = sqlx::query(
"INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)"
)
.bind(username)
.bind(email)
.bind("hashed_password")
.execute(pool)
.await?;
Ok(result.0 as i64)
}
// 并发创建多个用户
async fn create_users_concurrently(pool: MySqlPool) {
let mut tasks = vec![];
for i in 1..=5 {
let pool = pool.clone(); // 连接池可以克隆
let task = tokio::spawn(async move {
let username = format!("user{}", i);
let email = format!("user{}@example.com", i);
match create_user(&pool, &username, &email).await {
Ok(id) => println!("创建用户 {} (ID: {})", username, id),
Err(e) => eprintln!("错误: {}", e),
}
});
tasks.push(task);
}
// 等待所有任务完成
for task in tasks {
task.await.unwrap();
}
}
#[tokio::main]
async fn main() -> Result<(), sqlx::Error> {
let pool = MySqlPool::connect("mysql://root:password@localhost/test").await?;
create_users_concurrently(pool).await;
Ok(())
}
示例 4:缓存服务(本项目 Redis 应用)
rust
use std::sync::Arc;
use tokio::sync::RwLock;
use std::collections::HashMap;
// 内存缓存(读多写少场景)
struct Cache {
data: Arc<RwLock<HashMap<String, String>>>,
}
impl Cache {
fn new() -> Self {
Self {
data: Arc::new(RwLock::new(HashMap::new())),
}
}
async fn get(&self, key: &str) -> Option<String> {
let data = self.data.read().await;
data.get(key).cloned()
}
async fn set(&self, key: String, value: String) {
let mut data = self.data.write().await;
data.insert(key, value);
}
async fn delete(&self, key: &str) {
let mut data = self.data.write().await;
data.remove(key);
}
}
impl Clone for Cache {
fn clone(&self) -> Self {
Self {
data: Arc::clone(&self.data),
}
}
}
#[tokio::main]
async fn main() {
let cache = Cache::new();
// 并发读写
let mut tasks = vec![];
// 写入任务
for i in 0..5 {
let cache = cache.clone();
let task = tokio::spawn(async move {
let key = format!("key{}", i);
let value = format!("value{}", i);
cache.set(key.clone(), value).await;
println!("写入: {}", key);
});
tasks.push(task);
}
// 读取任务
for i in 0..10 {
let cache = cache.clone();
let task = tokio::spawn(async move {
let key = format!("key{}", i % 5);
if let Some(value) = cache.get(&key).await {
println!("读取: {} = {}", key, value);
}
});
tasks.push(task);
}
for task in tasks {
task.await.unwrap();
}
}
同步原语对比
| 类型 | 用途 | 开销 | 使用场景 |
|---|---|---|---|
Mutex<T> |
互斥访问 | 中等 | 共享可变状态 |
RwLock<T> |
读写锁 | 较高 | 读多写少 |
Atomic* |
原子操作 | 极低 | 简单计数器 |
Arc<T> |
引用计数 | 低 | 共享只读数据 |
mpsc::channel |
消息传递 | 中等 | 线程间通信 |
Barrier |
屏障同步 | 低 | 等待所有线程 |
Condvar |
条件变量 | 低 | 等待特定条件 |
异步 vs 线程
| 特性 | 线程 (Thread) | 异步 (Async) |
|---|---|---|
| 开销 | 较高(每个线程 ~2MB) | 极低(每个任务 ~KB) |
| 上下文切换 | OS 调度 | 用户态调度 |
| 适用场景 | CPU 密集型 | I/O 密集型 |
| 阻塞操作 | 可以使用 | 不能阻塞 |
| 并发数量 | 有限(数百) | 极大(数万) |
最佳实践
1. 选择合适的并发模型
rust
// CPU 密集型:使用线程
use std::thread;
let handle = thread::spawn(|| {
// 计算密集型任务
(1..1_000_000).sum::<i64>()
});
// I/O 密集型:使用异步
use tokio;
let result = tokio::spawn(async {
// 网络请求、文件读写等
fetch_data().await
});
2. 避免死锁
scss
// ❌ 错误:可能死锁
let data1 = mutex1.lock().unwrap();
let data2 = mutex2.lock().unwrap(); // 如果其他线程先锁 mutex2
// ✅ 正确:统一锁顺序
fn safe_operation(mutex1: &Mutex<i32>, mutex2: &Mutex<i32>) {
let data1 = mutex1.lock().unwrap();
let data2 = mutex2.lock().unwrap();
// 使用数据
}
3. 最小化锁持有时间
ini
// ❌ 锁持有时间过长
{
let mut data = mutex.lock().unwrap();
expensive_computation(&mut data);
}
// ✅ 缩短锁持有时间
let result = expensive_computation_without_lock();
{
let mut data = mutex.lock().unwrap();
*data = result;
}
4. 使用 RAII 自动释放资源
csharp
// 锁会在作用域结束时自动释放
{
let data = mutex.lock().unwrap();
// 使用 data
} // 锁自动释放
总结
核心概念
- 所有权系统防止数据竞争 - 编译期保证
- Send 和 Sync trait - 控制跨线程传递
- 消息传递优于共享内存 - Channel 优先
- 零成本抽象 - 性能无损失
常用模式
- Arc + Mutex - 共享可变状态
- mpsc::channel - 线程间通信
- tokio::spawn - 异步并发
- Semaphore - 限制并发数
本项目应用
- ✅ Tokio 异步运行时 -
#[tokio::main] - ✅ 数据库连接池 - 多线程安全的
MySqlPool - ✅ Redis 连接管理 -
Arc<RedisCache> - ✅ 并发请求处理 - Axum 异步 handlers
- ✅ gRPC 流式通信 -
tokio_stream
延伸阅读
- The Rust Book - Concurrency
- Tokio Tutorial
- Async Book
- 本项目代码:
src/main.rs,src/grpc/,src/services/