Rust中的并发与多线程:编写安全且快速的代码
现代程序很少以单线程模式运行。请求处理、文件操作、网络通信——所有这些都需要并行执行任务。Rust提供了一种独特的并发方法:它在编译阶段保证内存安全,防止数据竞争。在本文中,我们将探讨如何在Rust中使用线程、通道、互斥锁和异步编程。
1. 多线程基础:创建和管理线程
在Rust中,线程通过标准库std::thread创建。每个线程独立执行,但编译器会严格检查变量的生命周期。
创建线程
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(100)); } });
for i in 1..5 { println!("主线程: {}", i); thread::sleep(Duration::from_millis(100)); }
handle.join().unwrap(); // 等待线程结束}在闭包中捕获所有权
如果线程使用外部作用域的变量,需要通过move转移所有权:
let data = vec![1, 2, 3];
let handle = thread::spawn(move || { println!("数据: {:?}", data);});
handle.join().unwrap();// println!("{:?}", data); // 错误!data已被移动2. 数据共享:互斥锁和锁
为了从多个线程安全地访问共享数据,Rust使用Mutex(互斥锁)与Arc(原子引用计数)结合。
Mutex和Arc示例
use std::sync::{Arc, Mutex};use std::thread;
fn main() { 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()); // 10}重要提示: Mutex在离开作用域时会自动解锁。这可以在编译层面防止死锁(deadlock)。
RwLock——适用于频繁读取
如果数据很少修改但经常读取,请使用RwLock:
use std::sync::RwLock;
let lock = RwLock::new(5);
{ let r1 = lock.read().unwrap(); let r2 = lock.read().unwrap(); // 可以同时读取 println!("{} {}", r1, r2);}
{ let mut w = lock.write().unwrap(); *w += 1; // 写入操作会阻塞读取}3. 通道:线程间消息传递
Rust通过通道(std::sync::mpsc)支持actor模型。这使得线程可以在没有共享内存的情况下交换数据。
通道示例
use std::sync::mpsc;use std::thread;
fn main() { let (tx, rx) = mpsc::channel();
thread::spawn(move || { let vals = vec!["hello", "from", "the", "thread"]; for val in vals { tx.send(val).unwrap(); thread::sleep(std::time::Duration::from_secs(1)); } });
for received in rx { println!("收到: {}", received); }}多个发送者
通过mpsc::Sender可以克隆发送者以用于多个线程:
let (tx, rx) = mpsc::channel();let tx1 = tx.clone();
thread::spawn(move || { tx1.send("线程 1").unwrap(); });thread::spawn(move || { tx.send("