面试题答案
一键面试原理阐述
- Send 和 Sync 特性:
Send
:标记类型可以安全地在不同线程间传递所有权。如果类型的所有数据都实现了Send
,那么该类型自动实现Send
。例如,i32
实现了Send
,因为它可以安全地在不同线程间传递。Sync
:标记类型可以安全地在多个线程间共享。如果类型的所有数据都实现了Sync
,那么该类型自动实现Sync
。例如,i32
也实现了Sync
,因为多个线程可以同时读取一个i32
。
- 避免数据竞争:
- 不可变共享:使用
&
引用在多个线程间共享数据,Rust 的借用检查器可以确保在同一时间只有只读引用存在,从而避免数据竞争。 - 可变独占:使用
mut
可变引用,但在同一时间只能有一个可变引用,这也由借用检查器保证。在多线程场景下,对于可变数据,可以使用Mutex
或RwLock
等同步原语。Mutex
提供独占访问可变数据的能力,而RwLock
允许多个线程同时读,但只允许一个线程写。
- 不可变共享:使用
示例代码
use std::sync::{Arc, Mutex};
use std::thread;
// 自定义类型
#[derive(Debug)]
struct MyType {
data: i32,
}
impl Send for MyType {}
impl Sync for MyType {}
fn main() {
let shared_data = Arc::new(Mutex::new(MyType { data: 0 }));
let mut handles = vec![];
for _ in 0..10 {
let data = Arc::clone(&shared_data);
let handle = thread::spawn(move || {
let mut my_type = data.lock().unwrap();
my_type.data += 1;
println!("Thread updated data: {:?}", my_type);
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let final_data = shared_data.lock().unwrap();
println!("Final data: {:?}", final_data);
}
在这个示例中:
- 定义了一个自定义类型
MyType
,并手动为它实现了Send
和Sync
特性(实际上,这里MyType
内部的i32
实现了Send
和Sync
,所以MyType
也可以自动推导实现这两个特性)。 - 使用
Arc<Mutex<MyType>>
来在多个线程间共享可变数据。Arc
是原子引用计数,用于在多个线程间共享数据,Mutex
用于保证同一时间只有一个线程可以修改MyType
的数据。 - 在每个线程中,通过
lock
方法获取锁(unwrap
用于简单处理可能的锁获取失败情况),然后修改MyType
的data
字段,从而保证了多线程环境下数据访问的安全性。