Rust并发编程
放在十年前,多线程编程可能还是一个少数人才掌握的核心概念,但是在今天,随着编程语言的不断发展,多线程、多协程、Actor 等并发编程方式已经深入人心,同时多线程编程的门槛也在不断降低,本章节我们来看看
在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();
}