- 使用同步原语实现多线程共享可变变量:
- 在Rust中,可以使用
Mutex
(互斥锁)来保护共享的可变变量。Mutex
确保在任何时刻只有一个线程可以访问被保护的数据,从而避免数据竞争。
- 以下是实现代码:
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let shared_variable = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let shared_variable_clone = Arc::clone(&shared_variable);
let handle = thread::spawn(move || {
let mut data = shared_variable_clone.lock().unwrap();
*data += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let final_value = shared_variable.lock().unwrap();
println!("Final value: {}", *final_value);
}
- 代码分析:
Arc
(原子引用计数)用于在多个线程间共享Mutex
。Arc
允许在多个线程间安全地共享数据,因为它内部的引用计数操作是原子的。
Mutex
包裹着共享的可变变量0
。lock
方法会尝试获取锁,如果锁可用,则返回一个MutexGuard
智能指针,它实现了Deref
和DerefMut
,可以像普通引用一样访问和修改内部数据。当MutexGuard
离开作用域时,锁会自动释放。
- 在
thread::spawn
闭包中,首先克隆Arc
,这样每个线程都有自己的Arc
实例指向同一个Mutex
。然后通过lock
方法获取锁并修改内部的共享变量。
- 主线程通过
join
等待所有子线程完成,最后获取锁并打印共享变量的最终值。
- 死锁风险及避免:
- 死锁风险:
- 死锁可能发生在多个线程以不同顺序获取多个锁的情况下。例如,如果线程A获取锁
Mutex1
,然后尝试获取锁Mutex2
,而线程B获取锁Mutex2
,然后尝试获取锁Mutex1
,就可能发生死锁。在上述代码中,如果我们引入第二个Mutex
,并且在不同线程中以不同顺序获取这两个Mutex
,就会出现死锁风险。
- 避免方法:
- 按固定顺序获取锁:确保所有线程以相同顺序获取多个锁。例如,如果有
Mutex1
和Mutex2
,所有线程都先获取Mutex1
,再获取Mutex2
。
- 使用
try_lock
:Mutex
提供了try_lock
方法,它尝试获取锁,如果锁不可用,立即返回Err
。可以利用这个方法编写逻辑来避免死锁,例如在获取多个锁时,如果一个锁获取失败,可以释放已经获取的锁并重新尝试。
// 示例使用try_lock避免死锁
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let mutex1 = Arc::new(Mutex::new(0));
let mutex2 = Arc::new(Mutex::new(0));
let mutex1_clone = Arc::clone(&mutex1);
let mutex2_clone = Arc::clone(&mutex2);
let handle1 = thread::spawn(move || {
loop {
match (mutex1_clone.try_lock(), mutex2_clone.try_lock()) {
(Ok(mut guard1), Ok(mut guard2)) => {
// 安全地访问和修改数据
*guard1 += 1;
*guard2 += 1;
break;
}
_ => {
// 锁获取失败,释放已获取的锁(如果有)
// 这里可以添加一些等待逻辑
}
}
}
});
let handle2 = thread::spawn(move || {
loop {
match (mutex1.try_lock(), mutex2.try_lock()) {
(Ok(mut guard1), Ok(mut guard2)) => {
*guard1 += 1;
*guard2 += 1;
break;
}
_ => {
// 锁获取失败,释放已获取的锁(如果有)
// 这里可以添加一些等待逻辑
}
}
}
});
handle1.join().unwrap();
handle2.join().unwrap();
}