异步函数:特性`std::marker::Send`没有为`std::sync::MutexGuard<'_,Client>`实现
我在异步中使用递归时遇到了一个奇怪的错误。
我可以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());
}
错误:
error: future cannot be sent between threads safely
--> src/main.rs:17:7
|
17 | }.boxed()
| ^^^^^ future created by async block is not `Send`
|
= help: within `impl futures::Future`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, Client>`
note: future is not `Send` as this value is used across an await
--> src/main.rs:15:9
|
14 | let c = client.lock().unwrap();
| - has type `std::sync::MutexGuard<'_, Client>` which is not `Send`
15 | do_something(client.clone()).await;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `c` maybe used later
16 | Ok(())
17 | }.boxed()
| - `c` is later dropped here
操场
回答
这里有两个问题,都源于这样一个事实,即标准库Mutex被设计为与常规阻塞代码一起使用,而不是异步代码:
- 锁定互斥锁会阻塞线程,这通常是您希望在异步代码中避免的,因为它可以阻止其他任务运行。
- 正如编译器错误中所见,通过锁定它获得的互斥锁不能在线程之间安全地共享,因此无论如何它都不会编译。
这两个问题都可以通过使用Mutex专为异步代码设计的代码来解决。由于您已经在使用futurescrate 中的某些内容,因此您可以使用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());
}
操场
THE END
二维码