跳到主要内容

Mutex

搜索

结构体 Mutex 

Source
pub struct Mutex<T: ?Sized> { /* private fields */ }
展开描述

一个异步的、类似 Mutex 的类型。

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

Tokio 的 Mutex 以保证 FIFO 的方式运行。 这意味着任务 调用 lock 方法的顺序 就是它们 获取 lock 的确切顺序。

§Which kind of mutex should you use?

与流行的观点相反, 在异步代码中 使用标准库的 普通 Mutex 是可行的, 而且通常是首选。

异步 mutex 相对于阻塞 mutex 所提供的特性是 能够在 .await 点之间 保持其锁定状态。 这使得 异步 mutex 比阻塞 mutex 更昂贵, 因此在可以使用 阻塞 mutex 的情况下, 应优先使用 阻塞 mutex。 异步 mutex 的主要用例 是 提供对 IO 资源 (如数据库连接) 的 共享可变访问。 如果 mutex 背后的值 只是数据, 通常适合使用 阻塞 mutex, 例如 标准库中的 那个 或 parking_lot 中的 那个。

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

一种常见模式是 将 Arc<Mutex<...>> 包装在一个 提供非异步方法 来 对其内部数据 执行操作的 结构体中, 并且仅在这些方法内部 对 mutex 调用 lockmini-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 在如何将锁分配给 内部数据方面 是“公平的”且可预测的。 每次迭代后, 锁会被释放并重新获取, 所以基本上, 每个线程 在将值递增一次后 会回到队伍的末尾。 请注意, 线程启动时 的 时序 存在一定的不可预测性, 但 一旦它们开始运行, 它们就会 可预测地 交替进行。 最后, 由于 在任何给定时间 只有 单个 有效的 lock, 因此 在修改内部值时 不存在 竞争条件的可能。

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

实现§

Source§

impl<T: ?Sized> Mutex<T>

Source

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

创建一个新的锁,初始状态为未锁定,可直接使用。

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





let lock = Mutex::new(5);
Source

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);
Source

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

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

如果互斥锁可立即获取,则此调用通常不会让出运行时。但在所有情况下这都不能保证。

§Cancel safety

此方法使用队列按请求顺序公平分配锁。取消对 lock 的调用会丢失在队列中的位置。

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





let mutex = Mutex::new(1);





let mut n = mutex.lock().await;


*n = 2;
Source

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 inside spawn_blocking() (or 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);


}
Source

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 inside spawn_blocking() (or 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);


}
Source

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;
Source

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);
Source

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;


}
Source

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);
Source

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 实现§

Source§

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

Source§

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

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

impl<T> Default for Mutex<T>
where T: Default,

Source§

fn default() -> Self

Returns the “default value” for a type. 更多信息
Source§

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

Source§

fn from(s: T) -> Self

从输入类型转换为此类型。
Source§

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

Source§

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>

Blanket 实现§

Source§

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

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. 更多信息
Source§

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

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. 更多信息
Source§

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

Source§

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

Mutably borrows from an owned value. 更多信息
Source§

impl<T> From<!> for T

Source§

fn from(t: !) -> T

从输入类型转换为此类型。
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

原样返回传入的参数。

Source§

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

Source§

fn into(self) -> U

调用 U::from(self)

也就是说,此转换的具体行为取决于 From<T> for U 的实现方式。

Source§

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

Source§

type Error = Infallible

转换出错时返回的类型。
Source§

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

执行转换。
Source§

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

Source§

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

转换出错时返回的类型。
Source§

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

执行转换。