Struct std::sync::Mutex 1.0.0[−][src]
pub struct Mutex<T: ?Sized> { /* fields omitted */ }
Expand description
互斥原语可用于保护共享数据
此互斥锁将阻止等待锁可用的线程。互斥锁也可以通过 new
构造函数进行静态初始化或创建。
每个互斥锁都有一个类型参数,表示它正在保护的数据。
只能通过从 lock
和 try_lock
返回的 RAII 保护来访问数据,这保证了只有在互斥锁被锁定时才可以访问数据。
Poisoning
此模块中的互斥锁实现了一种称为 “poisoning” 的策略,只要线程 panics 按住互斥锁,互斥锁就会被视为中毒。 一旦互斥锁中毒,默认情况下,所有其他线程都无法访问数据,因为它很可能已被污染 (某些不变性未得到维护)。
对于互斥锁,这意味着 lock
和 try_lock
方法返回一个 Result
,该 Result
指示互斥锁是否已中毒。
互斥锁的大多数用法将只是 unwrap()
这些结果,从而在线程之间传播 panics 以确保不会看到可能无效的不变式。
但是,中毒的互斥锁不会阻止对底层数据的所有访问。
PoisonError
类型具有 into_inner
方法,该方法将返回保护,否则将在成功锁定后返回该保护。
尽管锁被中毒,这仍允许访问数据。
Examples
use std::sync::{Arc, Mutex}; use std::thread; use std::sync::mpsc::channel; const N: usize = 10; // Spawn 几个线程用于递增共享变量 (non-atomically),并在完成所有递增操作后让主线程知道。 // 在这里,我们使用 Arc 在线程之间共享内存,并且 Arc 中的数据受到互斥锁的保护。 let data = Arc::new(Mutex::new(0)); let (tx, rx) = channel(); for _ in 0..N { let (data, tx) = (Arc::clone(&data), tx.clone()); thread::spawn(move || { // 只有持有锁后,才能访问共享状态。 // 我们的非原子增量是安全的,因为当持有锁时,我们是唯一可以访问共享状态的线程。 // 我们用 unwrap() 的返回值来断言,我们不希望线程在持有锁的同时失败。 let mut data = data.lock().unwrap(); *data += 1; if *data == N { tx.send(()).unwrap(); } // `data` 离开作用域时,此处的锁已解锁。 }); } rx.recv().unwrap();Run
要从中毒的互斥锁中恢复:
use std::sync::{Arc, Mutex}; use std::thread; let lock = Arc::new(Mutex::new(0_u32)); let lock2 = Arc::clone(&lock); let _ = thread::spawn(move || -> () { // 该线程将首先获取互斥锁,因为该锁尚未中毒,所以将解开 `lock` 的结果。 let _guard = lock2.lock().unwrap(); // 按住锁 (`_guard` 在作用域中) 时,此 panic 将中毒互斥锁。 panic!(); }).join(); // 到此为止,锁定都会中毒,但是可以对返回的结果进行模式匹配,以返回两个分支上的基础防护。 let mut guard = match lock.lock() { Ok(guard) => guard, Err(poisoned) => poisoned.into_inner(), }; *guard += 1;Run
时需要手动丢弃互斥锁守卫,以便在封闭作用域结束之前将其解锁。
use std::sync::{Arc, Mutex}; use std::thread; const N: usize = 3; let data_mutex = Arc::new(Mutex::new(vec![1, 2, 3, 4])); let res_mutex = Arc::new(Mutex::new(0)); let mut threads = Vec::with_capacity(N); (0..N).for_each(|_| { let data_mutex_clone = Arc::clone(&data_mutex); let res_mutex_clone = Arc::clone(&res_mutex); threads.push(thread::spawn(move || { let mut data = data_mutex_clone.lock().unwrap(); // 这是一些重要而长期的工作的结果。 let result = data.iter().fold(0, |acc, x| acc + x * 2); data.push(result); drop(data); *res_mutex_clone.lock().unwrap() += result; })); }); let mut data = data_mutex.lock().unwrap(); // 这是一些重要而长期的工作的结果。 let result = data.iter().fold(0, |acc, x| acc + x * 2); data.push(result); // 我们明确丢弃 `data`,因为不再需要 `data`,并且线程仍然有工作要做。 // 这允许其他线程立即开始处理数据,而无需等待其余无关工作在这里完成。 // 它在这里比在线程中更重要,因为在此之后我们对线程进行 `.join` 处理。 // 如果我们没有丢弃互斥锁守卫,则线程可能会永远等待它,从而导致死锁。 drop(data); // 这里互斥锁防护未分配给变量,因此,即使作用域在此行之后没有结束,互斥锁仍被释放: 没有死锁。 *res_mutex.lock().unwrap() += result; threads.into_iter().for_each(|thread| { thread .join() .expect("The thread creating or execution failed !") }); assert_eq!(*res_mutex.lock().unwrap(), 800);Run
Implementations
获取一个互斥锁,阻塞当前线程,直到能够这样做为止。
该函数将阻塞本地线程,直到可用于获取互斥锁为止。 返回时,该线程是唯一持有锁的线程。 返回了 RAII 守卫,以允许对锁进行一定范围的解锁。 当守卫离开作用域时,互斥锁将被解锁。
未指定将互斥锁锁定在已经持有该锁的线程中的确切行为。 但是,该函数不会在第二次调用时返回 (例如,可能为 panic 或死锁)。
Errors
如果互斥锁的另一个用户在握住互斥锁时恐慌,则一旦获取互斥锁,此调用将返回错误。
Panics
如果当前线程已锁定,则调用此函数时可能为 panic。
Examples
use std::sync::{Arc, Mutex}; use std::thread; let mutex = Arc::new(Mutex::new(0)); let c_mutex = Arc::clone(&mutex); thread::spawn(move || { *c_mutex.lock().unwrap() = 10; }).join().expect("thread::spawn failed"); assert_eq!(*mutex.lock().unwrap(), 10);Run
尝试获取此锁。
如果此时无法获取锁,则返回 Err
。
否则,将返回 RAII 守卫。当守卫被丢弃时,锁将被解锁。
该函数不会阻止。
Errors
如果该调用的另一个用户同时持有互斥锁,则该调用将返回 Poisoned
错误,否则将获得该调用的拒绝互锁。
如果互斥锁已被锁定而无法获取,则该调用将返回 WouldBlock
错误。
Examples
use std::sync::{Arc, Mutex}; use std::thread; let mutex = Arc::new(Mutex::new(0)); let c_mutex = Arc::clone(&mutex); thread::spawn(move || { let mut lock = c_mutex.try_lock(); if let Ok(ref mut mutex) = lock { **mutex = 10; } else { println!("try_lock failed"); } }).join().expect("thread::spawn failed"); assert_eq!(*mutex.lock().unwrap(), 10);Run
确定互斥锁是否中毒。
如果另一个线程处于活动状态,则互斥锁仍可随时中毒。
如果没有其他同步,则不应信任 false
值来确保程序正确性。
Examples
use std::sync::{Arc, Mutex}; use std::thread; let mutex = Arc::new(Mutex::new(0)); let c_mutex = Arc::clone(&mutex); let _ = thread::spawn(move || { let _lock = c_mutex.lock().unwrap(); panic!(); // 互斥锁中毒 }).join(); assert_eq!(mutex.is_poisoned(), true);Run