std::sync::Mutex
创建Mutex实例
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
println!("m = {:?}", m);
}
m = Mutex { data: 5, poisoned: false, .. }
访问数据
get_mut
无需加锁,返回内部数据的可变引用。
在多线程下无法使用,需要使用
Arc+lock
use std::sync::Mutex;
fn main() {
let mut m = Mutex::new(5);
let v = m.get_mut().unwrap();
*v = 6;
println!("m: {m:?}");
}
m: Mutex { data: 6, poisoned: false, .. }
into_inner
消费
Mutex,返回内部数据(所有权转移)使用后就Mutex就无效了
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
let v = m.into_inner().unwrap();
println!("v: {v}");
// println!("m: {m:?}"); 无法使用,转移所有权了
}
v: 5
加锁
获取独占锁lock
阻塞调用,如果锁已被其他线程持有,当前线程会阻塞等待
MutexGuard离开作用域会自动解锁,不需要手动 unlock
use std::sync::Mutex;
fn main() {
let m = Mutex::new(5);
{
// lock获取独占锁
let mut num = m.lock().unwrap();
*num = 6;
} // 离开作用域解锁
println!("m = {:?}", m);
}
m = Mutex { data: 6, poisoned: false, .. }
案例
多线程操作加锁,只允许一个操作
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let m = Arc::new(Mutex::new(0));
let m1 = Arc::clone(&m);
let m2 = Arc::clone(&m);
let h1 = thread::spawn(move || {
for i in 0..3 {
println!("g1 等待: {}", i);
let mut g1 = m1.lock().unwrap();
println!("g1 获取锁: {}", i);
*g1 += 2;
thread::sleep(std::time::Duration::from_secs(1));
}
});
let h2 = thread::spawn(move || {
for i in 0..3 {
println!("g2 等待: {}", i);
let mut g2 = m2.lock().unwrap();
println!("g2 获取锁: {}", i);
*g2 -= 1;
thread::sleep(std::time::Duration::from_secs(1));
}
});
h1.join().unwrap();
h2.join().unwrap();
println!("{}", m.lock().unwrap())
}
g1 等待: 0
g1 获取锁: 0
g2 等待: 0
g1 等待: 1
g2 获取锁: 0
g2 等待: 1
g2 获取锁: 1
g2 等待: 2
g1 获取锁: 1
g1 等待: 2
g2 获取锁: 2
g1 获取锁: 2
3
非阻塞尝试加锁try_lock
尝试获取锁,如果获取失败则返回错误,不会阻塞线程
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let m = Arc::new(Mutex::new(0));
let m1 = Arc::clone(&m);
let m2 = Arc::clone(&m);
let h1 = thread::spawn(move || {
for i in 0..3 {
println!("g1[{}] 等待", i);
match m1.try_lock() {
Ok(mut guard) => {
*guard += 1;
println!("g1[{}] 成功获取锁 +1,值为: {}", i, *guard);
}
Err(_) => {
println!("g1[{}] 无法获取锁,锁正被其他线程占用", i);
}
}
}
});
let h2= thread::spawn(move || {
for i in 0..3 {
println!("g2[{}] 等待", i);
match m2.try_lock() {
Ok(mut guard) => {
*guard -= 1;
println!("g2[{}] 成功获取锁 -1,值为: {}", i, *guard);
}
Err(_) => {
println!("g2[{}] 无法获取锁,锁正被其他线程占用", i);
}
}
}
});
h1.join().unwrap();
h2.join().unwrap();
println!("{}", m.lock().unwrap())
}
g1[0] 等待
g2[0] 等待
g1[0] 成功获取锁 +1,值为: 1
g1[1] 等待
g1[1] 无法获取锁,锁正被其他线程占用
g1[2] 等待
g1[2] 无法获取锁,锁正被其他线程占用
g2[0] 成功获取锁 -1,值为: 0
g2[1] 等待
g2[1] 成功获取锁 -1,值为: -1
g2[2] 等待
g2[2] 成功获取锁 -1,值为: -2
-2
中毒
如果一个
线程拿着锁的时候突然``panic崩溃了,rust会这把锁打上标记poisoned`
is_poisoned
判断是否中毒
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let m = Arc::new(Mutex::new(0));
let m_clone = Arc::clone(&m);
// 子线程
let handle = thread::spawn(move || {
let _guard = m_clone.lock().unwrap();
panic!("oops"); // 持有锁时 panic
});
// 等待线程崩溃
let _ = handle.join();
println!("是否中毒: {}", m.is_poisoned());
}
thread '<unnamed>' (18672853) panicked at src/main.rs:11:9:
oops
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
是否中毒: true
中毒处理
恢复数据
poison_err中获取guard使用
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let m = Arc::new(Mutex::new(0));
let m_clone = Arc::clone(&m);
// 子线程
let handle = thread::spawn(move || {
let _guard = m_clone.lock().unwrap();
panic!("oops"); // 持有锁时 panic
});
// 等待线程崩溃
let _ = handle.join();
// 主线程阻塞,等待m_lock解锁
match m.lock() {
Ok(guard) => println!("正常: {}", guard),
Err(poison_err) => {
println!("锁已中毒,但仍可恢复数据");
// 内部获取数据
let guard = poison_err.into_inner();
let data = *guard;
println!("恢复的数据: {}", data);
}
}
}
thread '<unnamed>' (18637523) panicked at src/main.rs:11:9:
oops
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
锁已中毒,但仍可恢复数据
恢复的数据: 0
恢复数据+清理中毒标记
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let m = Arc::new(Mutex::new(0));
let m_clone = Arc::clone(&m);
// 子线程
let handle = thread::spawn(move || {
let _guard = m_clone.lock().unwrap();
panic!("oops"); // 持有锁时 panic
});
// 等待线程崩溃
let _ = handle.join();
// 主线程阻塞,等待m_lock解锁
match m.lock() {
Ok(guard) => println!("正常: {}", guard),
Err(poison_err) => {
println!("m中毒: {}", m.is_poisoned());
println!("锁已中毒,但仍可恢复数据");
// 内部获取数据
let mut guard = poison_err.into_inner();
*guard = 10;
// 清除中毒标记
m.clear_poison();
}
}
println!("m中毒: {}", m.is_poisoned());
}
thread '<unnamed>' (18688000) panicked at src/main.rs:11:9:
oops
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
m中毒: true
锁已中毒,但仍可恢复数据
m中毒: true
加锁机制流程图
Mutex 盒子
├── lock() → 返回 MutexGuard(钥匙)
├── try_lock() → 返回 MutexGuard(钥匙)
├── poison_err.into_inner() → 返回 MutexGuard(钥匙)
└── into_inner() → 直接拿出数据,锁没了
graph TD
AA[Mutex 盒子] --> A
A[lock/try_lock] -->|加锁| B[已被其他线程持有]
A --> |加锁| C[未被持有]
B -->|lock| A1[阻塞线程等待]
B -->|try_lock| A2[不阻塞线程,直接返回]
A2 -->|返回| A21["Err(WouldBlock)"]
C -->|检查是否中毒| C1[未中毒]
C -->|检查是否中毒| C2[中毒]
C1 -->|lock和try_lock获取钥匙| C11["Ok(MutexGuard)"]
C2 -->|错误内容| C12("Err(PoisonError)")
C11 -->|返回| C111[可以安全读写数据]
C12 -->|返回| C121[可选择恢复数据或放弃]
C121 -->|恢复数据| C1211["poison_err.into_inner()"]
C1211 -->|poison_err获取钥匙| C12111("MutexGuard")
C1211 -->|清除中毒标记| C121111["mutex.clear_poison()"]
C121 -->|放弃数据| C1212[离开作用域丢弃]
Mutex和RwLock区别
-
Mutex:
- 同一时刻 1 个线程访问
- 读写都独占
- 简单、通用
-
RwLock:
- 读:共享,多个线程同时读
- 写:独占,同一时刻只能 1 个写
- 写和读互斥(写的时候不能读取)

浙公网安备 33010602011771号