Rust 的线程与 Go 的 goroutine 有一些显著的区别:
- Rust 标准库线程(
std::thread
)
- 是操作系统级别的线程
- 每个线程都有独立的系统线程 ID
- 创建和销毁开销较大
- 不是轻量级的
- 需要手动管理线程同步和通信
示例:
use std::thread;
use std::sync::mpsc;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let val = String::from("hi");
tx.send(val).unwrap();
});
let received = rx.recv().unwrap();
println!("Got: {}", received);
}
- 第三方库提供的轻量级线程
tokio
提供类似 goroutine 的协程async-std
也提供轻量级异步运行时- 这些库通过异步运行时模拟轻量级线程
示例(tokio):
#[tokio::main]
async fn main() {
let handle = tokio::spawn(async {
// 类似 goroutine 的轻量级任务
println!("Hello from async task");
});
handle.await.unwrap();
}
- 与 Go 的 goroutine 对比
- Go 的 goroutine 是语言级别的轻量级线程
- Rust 需要通过第三方库实现类似功能
- Go 的 goroutine 由运行时调度
- Rust 的异步任务也是由运行时(如 tokio)调度
- 资源共享
- Rust 通过所有权和借用规则严格控制资源访问
- 编译器在编译期检查并发安全
- 需要使用
Arc
、Mutex
等进行安全的跨线程共享
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!("Result: {}", *counter.lock().unwrap());
}
总结:
- Rust 标准库线程不是轻量级的
- 需要通过异步运行时库模拟轻量级线程
- 提供更安全的并发原语
- 编译期检查并发安全