在Rust多线程编程中,同步性极其的重要,当你需要同时访问一个资源、控制不同线程的执行次序时,都需要使用到同步性。
在 Rust 中有多种方式可以实现同步性。简单来说,无非两种实现方式:
- 消息传递类似一个单所有权的系统:一个值同时只能有一个所有者,如果另一个线程需要该值的所有权,需要将所有权通过消息传递进行转移。例如我们可以通过消息传递来控制不同线程间的执行次序。
- 共享内存类似于一个多所有权的系统:多个线程可以同时访问同一个值。例如通过锁和原子操作等并发原语来实现多个线程同时且安全地去访问一个资源。
今天我想分享使用共享内存的方式来实现多线程的通信:
// Mutex互斥锁
use std::sync::{Mutex, Arc,Condvar};
use std::thread::{self,sleep};
use std::sync::RwLock;
use std::time::Duration;
// 单线程使用mutex
fn single_mutex() {
// 使用`Mutex`结构体的关联函数创建新的互斥锁实例
let m = Mutex::new(5);
{
// 获取锁,然后deref为`m`的引用
// lock返回的是Result
let mut num = m.lock().unwrap(); // m.lock()向m申请一个锁, 该方法会阻塞当前线程,直到获取到锁,因此当多个线程同时访问该数据时,只有一个线程能获取到锁,其它线程只能阻塞着等待
// m.lock()方法也有可能报错,例如当前正在持有锁的线程panic了。在这种情况下,其它线程不可能再获得锁,因此lock方法会返回一个错误。
*num = 6; //准确的说是m.lock()返回一个智能指针MutexGuard<T>
// 锁自动被drop
}
println!("m = {:?}", m);
}
// 如果把上面的括号去掉,同时申请两把锁,就会出现死锁
fn dead_mutex() {
let m = Mutex::new(5);
let mut num = m.lock().unwrap();
*num = 6;
// 锁还没有被 drop 就尝试申请下一个锁,导致主线程阻塞
drop(num); // 手动 drop num ,可以让 num1 申请到下个锁
let mut num1 = m.lock().unwrap();
*num1 = 7;
drop(num1); // 手动 drop num1 ,观察打印结果的不同
println!("m = {:?}", m); // m = Mutex { data: 7, poisoned: false, .. }
}
// 只能使用Arc<T>,实现多线程访问
fn mult_mutex() {
// 在使用数据前必须先获取锁
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()); // Result: 10
}
fn main() {
single_mutex();
dead_mutex();
mult_mutex();
}