使用互斥锁的异步递归函数

3
如何创建一个带有互斥锁的异步递归函数? Rust声称这段代码在await点(即暂停执行处)保留了互斥锁,然而该值在.await之前被释放。
#[async_recursion]
async fn f(mutex: &Arc<Mutex<u128>>) {
    let mut unwrapped = mutex.lock().unwrap();
    *unwrapped += 1;
    let value = *unwrapped;
    drop(unwrapped);
    tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
    if value < 100 {
        f(mutex);
    }
}

错误

future cannot be sent between threads safely
within `impl futures::Future<Output = ()>`, the trait `std::marker::Send` is not implemented for `std::sync::MutexGuard<'_, u128>`
required for the cast to the object type `dyn futures::Future<Output = ()> + std::marker::Send`rustc
lib.rs(251, 65): future is not `Send` as this value is used across an await
1个回答

3
在这种情况下,您可以重构代码,以使unwrapped不能在await之间使用:
let value = {
    let mut unwrapped = mutex.lock().unwrap();
    *unwrapped += 1;
    *unwrapped
};
tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;
if value < 100 {
    f(mutex);
}

如果你不能这样做,那么你需要使其不返回实现 SendFutureasync_recursion 文档 指定了一个选项,你可以传递给宏来禁用它添加的 Send 约束。
#[async_recursion(?Send)]
async fn f(mutex: &Arc<Mutex<u128>>) {
    ...

(游乐场)

然而,您无法在线程之间发送这样的Future


这不很危险吗?发送存在是有原因的。 - Test
@Test 我加入了一种修复此问题的方法,而不需要使用 ?Send。虽然第二种方式并不危险,但编译器会在您尝试发送无法在线程之间发送且未实现 Send 特性的类型时停止您。 - smitop
好的,谢谢。为什么 drop 没有达到同样的效果呢? - Test
2
@Test 我的理解是编译器无法推断 drop(unwrapped) 实际上是否丢弃了 unwrapped,而不是可能将其发送到另一个线程,在 await 点之后再使用它(drop 只是一个普通函数)。 - smitop
1
@Smitop 虽然 drop 是一个普通函数,但它会移动值并且编译器知道这一点。对代码进行一些小的调整表明它是 async_recursion 使用的生命周期,但我不确定。在异步函数中跟踪 Drop 的方式被认为过于保守 - Chayim Friedman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接