异步函数:特性 `std::marker::Send` 没有为 `std::sync::MutexGuard<'_, Client>` 实现

时间:2021-04-27 05:10:42

标签: rust

我在异步中使用递归时遇到了一个奇怪的错误。

我可以在 True 中使用 client。但是,如果我再次调用 async,它会抱怨未发送 do_something。我认为它正在尝试将 std::sync::MutexGuard<'_, Client> 发送到另一个线程。我认为 c 在一个线程中执行,而 do_something 在另一个线程中执行。我不明白为什么 do_something(client.clone()).await 会从 c 话题转移到这个新话题。

async

错误:

use std::sync::{Arc, Mutex};
use std::future::Future;
use futures::future::{BoxFuture, FutureExt};

struct Client{
}

impl Client {}

fn do_something<'a>(
        client: Arc<Mutex<Client>>,
) -> BoxFuture<'a, std::result::Result<(), ()>> {
    async move {
        let c = client.lock().unwrap();
        do_something(client.clone()).await;
        Ok(())
    }.boxed()
}

fn main() {
    let c = Arc::new(Mutex::new(Client{}));
    do_something(c.clone());
}

Playground

1 个答案:

答案 0 :(得分:3)

这里有两个问题,都源于标准库 Mutex 被设计为与常规阻塞代码一起使用,而不是异步代码:

  • 锁定互斥锁会阻塞线程,这通常是您希望在异步代码中避免的,因为它会阻止其他任务运行。
  • 如编译器错误所示,您通过锁定它获得的互斥保护不能在线程之间安全地共享,因此无论如何它都不会编译。

这两个问题都可以通过使用为异步代码设计的 Mutex 来解决。由于您已经在使用 futures 板条箱中的某些东西,您可以只使用 futures::lock::Mutex,将 client.lock().unwrap() 更改为 client.lock().await。当需要等待而不是阻塞线程时,此版本的锁会将控制权交还给任务执行器,并且还允许在必要时在线程之间共享。

代码的完整版本看起来像这样 - 但是,它不是很有用,因为您实际上从未真正执行 do_something 返回的未来(请注意,您没有对返回的未来做任何事情)并且即使你这样做了,它也会因为递归而立即死锁:

use std::sync::Arc;
use std::future::Future;
use futures::future::{BoxFuture, FutureExt};
use futures::lock::Mutex;

struct Client {}

impl Client {}

fn do_something<'a>(
        client: Arc<Mutex<Client>>,
) -> BoxFuture<'a, std::result::Result<(), ()>> {
    async move {
        let c = client.lock().await;
        do_something(client.clone()).await;
        Ok(())
    }.boxed()
}

fn main() {
    let c = Arc::new(Mutex::new(Client {}));
    do_something(c.clone());
}

Playground