1. 概述
rust语言本身的并发特性较少,目前讲的并发特性都是来自标准库(而不是语言本身)。
其实我们无需仅限于标准库的并发,可以自己实现并发。
但在rust语言中有两个并发概念:
std::marker::Sync
和std::marker::Send
这两个trait。
2. Send
Send:允许线程间转移所有权。
- 实现Send trait的类型可以在线程间转移所有权,Rust中几乎所有的类型都实现了Send
- 但
Rc<T>
没有实现Send,它只用于单线程的场景。
- 但
- 任何完全由Send组成的类型也被标记为Send
- 实际上除了原始指针之外,几乎所有的基础类型都实现了Send
例如:
rust
use std::thread;
fn main() {
let number = 5;
let handle = thread::spawn(move || {
println!("{}", number); // 这里可以正常工作,因为number实现了Send trait
});
println!("{}", number);
handle.join().unwrap();
}
我们创建了一个新的线程,并把一个number
变量移动到这个新线程中。这是因为number
的类型(在这个例子中是i32
)实现了Send
trait,所以我们可以安全地在两个线程之间转移所有权的所有权。
3. Sync
Sync:允许从多线程同时访问。
- 实现Sync的类型可以安全的被多个线程引用
- 也就是说:如果
T
实现了Sync
,那么&T
(T的引用)就是实现了Send
- 表示
T
的引用可以被安全地送往另一个线程
- 表示
- 基础类型都实现了Sync
- 完全由Sync类型组成的类型也是Sync
- 但是
Rc<T>
不是Sync RefCell<T>
和Cell<T>
家族也不是Sync- 而
Mutex<T>
实现了Sync
- 但是
rust
use std::sync::Arc;
use std::thread;
fn main() {
let shared_data = Arc::new(Some(5)); // Arc封装了一个可共享的数据,并且可以安全地在多个线程之间共享。
let handle = thread::spawn(move || {
println!("{:?}", shared_data); // 这里可以正常工作,因为Arc<T>实现了Sync trait
});
handle.join().unwrap();
}
我们创建了一个新的线程,并尝试在两个线程之间共享一个Arc<T>
对象。这是因为Arc<T>
(通过其内部的类型T
)实现了Sync
trait,所以我们可以安全地在多个线程之间共享这个对象。
如果我们尝试在两个线程之间直接共享一个没有实现Sync
的对象,Rust编译器会给出错误。
4. 手动实现Send和Sync很难保证安全
手动来实现Send和Sync是很难保证安全的,因为手动实现这些trait的时候涉及到使用特殊不安全的Rust代码,你需要非常谨慎地确保设计,才能满足线程间的安全性要求。