pub struct Mutex<T: ?Sized> { /* private fields */ }
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;
// 产生几个线程来增加一个共享变量 (非原子的),并在完成所有递增操作后,就让主线程知道。
// 在这里,我们使用 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();
// 持有锁时的这种 panic (`_guard` 在作用域内) 将使互斥锁中毒。
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 || {
// 这里我们用一个 block 来限制锁卫的生命周期。
let result = {
let mut data = data_mutex_clone.lock().unwrap();
// 这是一些重要而长期的工作的结果。
let result = data.iter().fold(0, |acc, x| acc + x * 2);
data.push(result);
result
// 交互锁守卫在这里得到抛弃,连同在关键部分创建的任何其他值。
};
// 这里创建的守卫是语句末尾的临时丢弃,即
// 即使线程做了一些额外的工作,锁也不会保持持有状态。
*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` 函数。
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);
RunImplementations§
source§impl<T: ?Sized> Mutex<T>
impl<T: ?Sized> Mutex<T>
sourcepub fn lock(&self) -> LockResult<MutexGuard<'_, T>>
pub fn lock(&self) -> LockResult<MutexGuard<'_, T>>
获取一个互斥锁,阻塞当前线程,直到能够这样做为止。
该函数将阻塞本地线程,直到可用于获取互斥锁为止。 返回时,该线程是唯一持有锁的线程。 返回了 RAII 守卫,以允许对锁进行一定范围的解锁。 当守卫离开作用域时,互斥锁将被解锁。
未指定将互斥锁锁定在已经持有该锁的线程中的确切行为。 但是,该函数不会在第二次调用时返回 (例如,可能为 panic 或死锁)。
Errors
如果互斥锁的另一个用户在持有互斥锁时 panic,那么一旦获取了互斥锁,这个调用将返回一个错误。
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);
Runsourcepub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>>
pub fn try_lock(&self) -> TryLockResult<MutexGuard<'_, T>>
尝试获取此锁。
如果此时无法获取锁,则返回 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);
Runsourcepub fn unlock(guard: MutexGuard<'_, T>)
🔬This is a nightly-only experimental API. (mutex_unlock
#81872)
pub fn unlock(guard: MutexGuard<'_, T>)
mutex_unlock
#81872)1.2.0 · sourcepub fn is_poisoned(&self) -> bool
pub fn is_poisoned(&self) -> bool
确定互斥锁是否中毒。
如果另一个线程处于活动状态,则互斥锁仍可随时中毒。
如果没有其他同步,则不应信任 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);
Runsourcepub fn clear_poison(&self)
🔬This is a nightly-only experimental API. (mutex_unpoison
#96469)
pub fn clear_poison(&self)
mutex_unpoison
#96469)从互斥锁中清除中毒状态
如果互斥锁中毒了,它将保持中毒状态,直到调用这个函数为止。 这允许从中毒状态中恢复并标记它已经恢复。 例如,如果该值被已知良好的值覆盖,则互斥锁可以被标记为未中毒。 或者可能,可以检查该值,以确定它是否处于一致状态,如果是,则会清除中毒状态。
Examples
#![feature(mutex_unpoison)]
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);
let x = mutex.lock().unwrap_or_else(|mut e| {
**e.get_mut() = 1;
mutex.clear_poison();
e.into_inner()
});
assert_eq!(mutex.is_poisoned(), false);
assert_eq!(*x, 1);
Run1.6.0 · sourcepub fn into_inner(self) -> LockResult<T>where
T: Sized,
pub fn into_inner(self) -> LockResult<T>where T: Sized,
1.6.0 · sourcepub fn get_mut(&mut self) -> LockResult<&mut T>
pub fn get_mut(&mut self) -> LockResult<&mut T>
Trait Implementations§
1.24.0 · source§impl<T> From<T> for Mutex<T>
impl<T> From<T> for Mutex<T>
source§fn from(t: T) -> Self
fn from(t: T) -> Self
在解锁状态下创建一个新的互斥锁,以备使用。
这等效于 Mutex::new
。