跳到主要内容

Mutex

搜索

结构体 Mutex 

源代码
pub struct Mutex<T: ?Sized> { /* 私有字段 */ }
展开描述

异步的类似 Mutex 的类型。

此类型的行为类似于 std::sync::Mutex,但有两个主要区别:lock 是一个异步方法,因此不会阻塞,并且锁保护器被设计为可以跨 .await 点持有。

Tokio 的 Mutex 在保证 FIFO(先进先出)的基础上运行。这意味着任务调用 lock 方法的顺序正是它们获取锁的顺序。

§Which kind of mutex should you use?

与普遍的看法相反,在异步代码中使用标准库的普通 Mutex 是可以的,而且通常是首选。

异步互斥锁相对于阻塞互斥锁提供的特性是能够跨 .await 点保持锁定。这使得异步互斥锁比阻塞互斥锁更昂贵,因此在使用阻塞互斥锁可行的场景下应首选阻塞互斥锁。异步互斥锁的主要用例是为 IO 资源(例如数据库连接)提供共享的可变访问。如果互斥锁后面的值只是数据,通常适合使用阻塞互斥锁,例如标准库中的互斥锁或 parking_lot

请注意,尽管编译器不会阻止 std Mutex 在任务在线程之间不可移动的情况下跨 .await 点持有其保护器,但在实践中这几乎从不会产生正确的并发代码,因为它很容易导致死锁。

一种常见的模式是将 Arc<Mutex<...>> 包装在一个结构体中,该结构体提供用于对其中的数据执行操作的非异步方法,并且仅在这些方法内部锁定互斥锁。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.");

在此示例中有几点需要注意。

  1. The mutex is wrapped in an Arc to allow it to be shared across threads.
  2. Each spawned task obtains a lock and releases it on every iteration.
  3. 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(先进先出)方式工作,其中所有对 lock 的调用都按其执行的顺序完成。这样,Mutex 在如何将锁分配给内部数据方面是“公平”且可预测的。每次迭代后都会释放并重新获取锁,所以基本上,每个线程在将值递增一次后都会回到队尾。请注意,线程启动的时序存在一些不可预测性,但一旦它们开始运行,它们就会以可预测的方式交替。最后,由于任何给定时间都只有一个有效的锁,因此在变更内部值时不可能发生竞态条件。

请注意,与 std::sync::Mutex 相反,当持有 MutexGuard 的线程发生 panic 时,此实现不会使互斥锁中毒。在这种情况下,互斥锁将被解锁。如果 panic 被捕获,则受互斥锁保护的数据可能会处于不一致的状态。

实现§

源代码§

impl<T: ?Sized> Mutex<T>

源代码

pub fn new(t: T) -> Self
where T: Sized,

以未锁定状态创建一个新锁,可直接使用。

§示例
use tokio::sync::Mutex;

let lock = Mutex::new(5);
源代码

pub const fn const_new(t: T) -> Self
where T: Sized,

以未锁定状态创建一个新锁,可直接使用。

当使用 tracing 不稳定特性时,使用 const_new 创建的 Mutex 将不会被检测。因此,它将不会出现在 tokio-console 中。如果需要,应改用 Mutex::new 创建一个已检测的对象。

§示例
use tokio::sync::Mutex;

static LOCK: Mutex<i32> = Mutex::const_new(5);
源代码

pub async fn lock(&self) -> MutexGuard<'_, T>

锁定此互斥锁,使当前任务让出,直到获取到锁为止。获取锁后,函数返回一个 MutexGuard

如果互斥锁可以立即获取,那么此调用通常不会让出执行权给运行时。但是,并非在所有情况下都能保证这一点。

§取消安全性

此方法使用一个队列来按请求顺序公平地分发锁。取消对 lock 的调用会使你失去在队列中的位置。

§示例
use tokio::sync::Mutex;

let mutex = Mutex::new(1);

let mut n = mutex.lock().await;
*n = 2;
源代码

pub fn blocking_lock(&self) -> MutexGuard<'_, T>

阻塞地锁定此 Mutex。获取锁后,函数返回一个 MutexGuard

此方法适用于需要在异步代码以及同步代码中使用此互斥锁的场景。

§恐慌

如果在异步执行上下文中调用,此函数会发生 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 inside spawn_blocking() (or [block_in_place()][crate::task::block_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);
}
源代码

pub fn blocking_lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>

阻塞地锁定此 Mutex。获取锁后,函数返回一个 OwnedMutexGuard

此方法与 Mutex::blocking_lock 相同,只是返回的保护器使用 Arc 而不是通过借用引用 Mutex。因此,Mutex 必须包装在 Arc 中才能调用此方法,并且保护器将在 'static 生命周期内有效,因为它通过持有 Arc 来保持 Mutex 存活。

§恐慌

如果在异步执行上下文中调用,此函数会发生 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 inside spawn_blocking() (or [block_in_place()][crate::task::block_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);
}
源代码

pub async fn lock_owned(self: Arc<Self>) -> OwnedMutexGuard<T>

锁定此互斥锁,使当前任务让出,直到获取到锁为止。获取锁后,此方法返回一个 OwnedMutexGuard

如果互斥锁可以立即获取,那么此调用通常不会让出执行权给运行时。但是,并非在所有情况下都能保证这一点。

此方法与 Mutex::lock 相同,只是返回的保护器使用 Arc 而不是通过借用引用 Mutex。因此,Mutex 必须包装在 Arc 中才能调用此方法,并且保护器将在 'static 生命周期内有效,因为它通过持有 Arc 来保持 Mutex 存活。

§取消安全性

此方法使用一个队列来按请求顺序公平地分发锁。取消对 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;
源代码

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);
源代码

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;
}
源代码

pub fn try_lock_owned( self: Arc<Self>, ) -> Result<OwnedMutexGuard<T>, TryLockError>

尝试获取锁,如果锁当前在其他位置被持有,则返回 TryLockError

此方法与 Mutex::try_lock 相同,只是返回的保护器使用 Arc 而不是通过借用引用 Mutex。因此,Mutex 必须包装在 Arc 中才能调用此方法,并且保护器将在 '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);
源代码

pub fn into_inner(self) -> T
where T: Sized,

消费互斥锁,返回底层数据。

§示例
use tokio::sync::Mutex;

let mutex = Mutex::new(1);

let n = mutex.into_inner();
assert_eq!(n, 1);

trait 实现§

源代码§

impl<T> Debug for Mutex<T>
where T: Debug + ?Sized,

源代码§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

使用给定的格式化器格式化此值。 更多信息
源代码§

impl<T> 默认值 for Mutex<T>
where T: 默认值,

源代码§

fn default() -> Self

Returns the “default value” for a type. 更多信息
源代码§

impl<T> From<T> for Mutex<T>

源代码§

fn from(s: T) -> Self

从输入类型转换为此类型。
源代码§

impl<T> Send for Mutex<T>
where T: ?Sized + Send,

源代码§

impl<T> Sync for Mutex<T>
where T: ?Sized + Send,

自动 trait 实现§

§

impl<T> !Freeze for Mutex<T>

§

impl<T> !RefUnwindSafe for Mutex<T>

§

impl<T> Unpin for Mutex<T>
where T: Unpin + ?Sized,

§

impl<T> UnsafeUnpin for Mutex<T>
where T: UnsafeUnpin + ?Sized,

§

impl<T> UnwindSafe for Mutex<T>
where T: UnwindSafe + ?Sized,

blanket 实现§

源代码§

impl<T> Any for T
where T: 'static + ?Sized,

源代码§

fn type_id(&self) -> TypeId

Gets the TypeId of self. 更多信息
源代码§

impl<T> Borrow<T> for T
where T: ?Sized,

源代码§

fn borrow(&self) -> &T

Immutably borrows from an owned value. 更多信息
源代码§

impl<T> BorrowMut<T> for T
where T: ?Sized,

源代码§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. 更多信息
源代码§

impl<T> From<!> for T

源代码§

fn from(t: !) -> T

从输入类型转换为此类型。
源代码§

impl<T> From<T> for T

源代码§

fn from(t: T) -> T

原样返回参数。

源代码§

impl<T, U> Into<U> for T
where U: From<T>,

源代码§

fn into(self) -> U

调用 U::from(self)

也就是说,此转换是 From<T> for U 实现选择执行的操作。

源代码§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

源代码§

type Error = Infallible

转换出错时返回的类型。
源代码§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

执行转换。
源代码§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

源代码§

type Error = <U as TryFrom<T>>::Error

转换出错时返回的类型。
源代码§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

执行转换。