我在异步中使用递归时遇到了一个奇怪的错误。
我可以在 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());
}
答案 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());
}