1. 概述
rust言语自身的并发特性较少,目前讲的并发特性都是来自规范库(而不是言语自身)。
其实咱们无需仅限于规范库的并发,能够自己完成并发。
但在rust言语中有两个并发概念:
-
std::marker::Sync
和std::marker::Send
这两个trait。
2. Send
Send:答应线程间搬运所有权。
- 完成Send trait的类型能够在线程间搬运所有权,Rust中简直所有的类型都完成了Send
- 但
Rc<T>
没有完成Send,它只用于单线程的场景。
- 但
- 任何完全由Send组成的类型也被标记为Send
- 实际上除了原始指针之外,简直所有的根底类型都完成了Send
例如:
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
- 但是
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代码,你需求非常谨慎地确保设计,才能满意线程间的安全性要求。