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 个写
    • 写和读互斥(写的时候不能读取)
posted @ 2026-04-14 21:58  lxd670  阅读(25)  评论(0)    收藏  举报