pub struct Mutex<T: ?Sized> { /* private fields */ }展开描述
一个异步的、类似 的类型。Mutex
此类型
行为类似于
,
但有两个主要区别:
std::sync::Mutex
是一个异步方法,
因此不会阻塞,
并且 locklock
guard
被设计为
可以跨
点
持有。.await
Tokio 的
Mutex
以保证 FIFO 的方式运行。
这意味着任务
调用
方法的顺序
就是它们
获取 locklock
的确切顺序。
§Which kind of mutex should you use?
与流行的观点相反,
在异步代码中
使用标准库的
普通
是可行的,
而且通常是首选。Mutex
异步 mutex
相对于阻塞 mutex
所提供的特性是
能够在
点之间
保持其锁定状态。
这使得
异步 mutex
比阻塞 mutex
更昂贵,
因此在可以使用
阻塞 mutex 的情况下,
应优先使用
阻塞 mutex。
异步 mutex 的主要用例
是
提供对 IO 资源
(如数据库连接)
的
共享可变访问。
如果 mutex
背后的值
只是数据,
通常适合使用
阻塞 mutex,
例如
标准库中的
那个
或
.await
中的
那个。parking_lot
请注意,
尽管在任务
不在线程间
移动的情况下,
编译器
不会阻止 std 的
在
Mutex
点之间
持有其 guard,
但实际上
这几乎从不会
产生正确的并发代码,
因为它很容易
导致死锁。.await
一种常见模式是
将
包装在一个
提供非异步方法
来
对其内部数据
执行操作的
结构体中,
并且仅在这些方法内部
对 mutex 调用 Arc<Mutex<...>>lock。
mini-redis 示例
演示了这种模式。
此外, 当你确实 想要共享访问 IO 资源时, 通常更好的做法是 派生一个任务 来管理该 IO 资源, 并使用消息传递 与该任务通信。
§Examples:
use tokio::sync::Mutex;
use std::sync::Arc;
let data1 = Arc::new(Mutex::new(0));
let data2 = Arc::clone(&data1);
tokio::spawn(async move {
let mut lock = data2.lock().await;
*lock += 1;
});
let mut lock = data1.lock().await;
*lock += 1;use tokio::sync::Mutex;
use std::sync::Arc;
let count = Arc::new(Mutex::new(0));
for i in 0..5 {
let my_count = Arc::clone(&count);
tokio::spawn(async move {
for j in 0..10 {
let mut lock = my_count.lock().await;
*lock += 1;
println!("{} {} {}", i, j, lock);
}
});
}
loop {
if *count.lock().await >= 50 {
break;
}
}
println!("Count hit 50.");在此示例中 有几点 需要注意。
- The mutex is wrapped in an
Arcto allow it to be shared across threads. - Each spawned task obtains a lock and releases it on every iteration.
- Mutation of the data protected by the Mutex is done by de-referencing the obtained lock as seen on lines 13 and 20.
Tokio 的 Mutex
以简单的 FIFO (先进先出)
方式工作,
其中
对
的所有调用
按
它们执行的顺序
完成。
这样
lockMutex
在如何将锁分配给
内部数据方面
是“公平的”且可预测的。
每次迭代后,
锁会被释放并重新获取,
所以基本上,
每个线程
在将值递增一次后
会回到队伍的末尾。
请注意,
线程启动时
的
时序
存在一定的不可预测性,
但
一旦它们开始运行,
它们就会
可预测地
交替进行。
最后,
由于
在任何给定时间
只有
单个
有效的
lock,
因此
在修改内部值时
不存在
竞争条件的可能。
请注意,
与
相反,
当持有
std::sync::Mutex
的线程
发生 panic 时,
此实现
不会
将 mutex
标记为已中毒。
在这种情况下,
mutex
会被解锁。
如果 panic 被捕获,
这可能会
使 mutex
保护的数据
处于
不一致的状态。MutexGuard
实现§
Source§impl<T: ?Sized> Mutex<T>
impl<T: ?Sized> Mutex<T>
Sourcepub const fn const_new(t: T) -> Selfwhere
T: Sized,
pub const fn const_new(t: T) -> Selfwhere
T: Sized,
创建一个新的锁,初始状态为未锁定,可直接使用。
使用 tracing 不稳定特性时,通过 const_new 创建的 Mutex 不会被插桩。因此,它不会出现在 tokio-console 中。如有需要,请改用 Mutex::new 来创建可插桩的对象。
§示例
use tokio::sync::Mutex;
static LOCK: Mutex<i32> = Mutex::const_new(5);Sourcepub async fn lock(&self) -> MutexGuard<'_, T>
pub async fn lock(&self) -> MutexGuard<'_, T>
Sourcepub fn blocking_lock(&self) -> MutexGuard<'_, T>
pub fn blocking_lock(&self) -> MutexGuard<'_, T>
阻塞地锁定此 Mutex。获取到锁后,函数返回一个 MutexGuard。
此方法用于需要在异步代码和同步代码中都使用此互斥锁的场景。
§Panics
如果在异步执行上下文中调用此函数会触发 panic。
- If you find yourself in an asynchronous execution context and needing
to call some (synchronous) function which performs one of these
blocking_operations, then consider wrapping that call insidespawn_blocking()(orblock_in_place()).
§示例
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() {
let mutex = Arc::new(Mutex::new(1));
let lock = mutex.lock().await;
let mutex1 = Arc::clone(&mutex);
let blocking_task = tokio::task::spawn_blocking(move || {
// This shall block until the `lock` is released.
let mut n = mutex1.blocking_lock();
*n = 2;
});
assert_eq!(*lock, 1);
// Release the lock.
drop(lock);
// Await the completion of the blocking task.
blocking_task.await.unwrap();
// Assert uncontended.
let n = mutex.try_lock().unwrap();
assert_eq!(*n, 2);
}Sourcepub fn blocking_lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>
pub fn blocking_lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>
阻塞地锁定此 Mutex。获取到锁后,函数返回一个 OwnedMutexGuard。
此方法与 Mutex::blocking_lock 相同,只是返回的 guard 通过 Arc 而非借用引用 Mutex。因此,调用此方法时 Mutex 必须包装在 Arc 中,且 guard 将在 'static 生命周期内有效,因为它通过持有 Arc 保持 Mutex 存活。
§Panics
如果在异步执行上下文中调用此函数会触发 panic。
- If you find yourself in an asynchronous execution context and needing
to call some (synchronous) function which performs one of these
blocking_operations, then consider wrapping that call insidespawn_blocking()(orblock_in_place()).
§示例
use std::sync::Arc;
use tokio::sync::Mutex;
#[tokio::main]
async fn main() {
let mutex = Arc::new(Mutex::new(1));
let lock = mutex.lock().await;
let mutex1 = Arc::clone(&mutex);
let blocking_task = tokio::task::spawn_blocking(move || {
// This shall block until the `lock` is released.
let mut n = mutex1.blocking_lock_owned();
*n = 2;
});
assert_eq!(*lock, 1);
// Release the lock.
drop(lock);
// Await the completion of the blocking task.
blocking_task.await.unwrap();
// Assert uncontended.
let n = mutex.try_lock().unwrap();
assert_eq!(*n, 2);
}Sourcepub async fn lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>
pub async fn lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>
锁定此互斥锁,使当前任务挂起直到获取到锁。获取到锁后,返回一个 OwnedMutexGuard。
如果互斥锁可立即获取,则此调用通常不会让出运行时。但在所有情况下这都不能保证。
此方法与 Mutex::lock 相同,只是返回的 guard 通过 Arc 而非借用引用 Mutex。因此,调用此方法时 Mutex 必须包装在 Arc 中,且 guard 将在 'static 生命周期内有效,因为它通过持有 Arc 保持 Mutex 存活。
§Cancel safety
此方法使用队列按请求顺序公平分配锁。取消对 lock_owned 的调用会丢失在队列中的位置。
§示例
use tokio::sync::Mutex;
use std::sync::Arc;
let mutex = Arc::new(Mutex::new(1));
let mut n = mutex.clone().lock_owned().await;
*n = 2;Sourcepub fn try_lock(&self) -> Result<MutexGuard<'_, T>, TryLockError>
pub fn try_lock(&self) -> Result<MutexGuard<'_, T>, TryLockError>
尝试获取锁,如果锁当前被其他位置持有则返回 TryLockError。
§示例
use tokio::sync::Mutex;
let mutex = Mutex::new(1);
let n = mutex.try_lock()?;
assert_eq!(*n, 1);Sourcepub fn get_mut(&mut self) -> &mut T
pub fn get_mut(&mut self) -> &mut T
返回对底层数据的可变引用。
由于此调用可变地借用 Mutex,不需要实际执行加锁 —— 可变借用静态保证不存在任何锁。
§示例
use tokio::sync::Mutex;
fn main() {
let mut mutex = Mutex::new(1);
let n = mutex.get_mut();
*n = 2;
}Sourcepub fn try_lock_owned(
self: Arc<Self>,
) -> Result<OwnedMutexGuard<T>, TryLockError>
pub fn try_lock_owned( self: Arc<Self>, ) -> Result<OwnedMutexGuard<T>, TryLockError>
尝试获取锁,如果锁当前被其他位置持有则返回 TryLockError。
此方法与 Mutex::try_lock 相同,只是返回的 guard 通过 Arc 而非借用引用 Mutex。因此,调用此方法时 Mutex 必须包装在 Arc 中,且 guard 将在 'static 生命周期内有效,因为它通过持有 Arc 保持 Mutex 存活。
§示例
use tokio::sync::Mutex;
use std::sync::Arc;
let mutex = Arc::new(Mutex::new(1));
let n = mutex.clone().try_lock_owned()?;
assert_eq!(*n, 1);Sourcepub fn into_inner(self) -> Twhere
T: Sized,
pub fn into_inner(self) -> Twhere
T: Sized,
消耗互斥锁,返回底层数据。
§示例
use tokio::sync::Mutex;
let mutex = Mutex::new(1);
let n = mutex.into_inner();
assert_eq!(n, 1);