在异步函数的匹配中获取死锁

Getting deadlock inside match of async function

我在以下示例中陷入僵局:

use tokio::net::TcpListener;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use futures::lock::Mutex;
use std::sync::Arc;

struct A{
    
}

impl A {
    pub async fn do_something(&self) -> std::result::Result<(), ()>{
        Err(())
    }
}

async fn lock_and_use(a: Arc<Mutex<A>>) {
    match a.clone().lock().await.do_something().await {
        Ok(()) => {
            
        },
        Err(()) => {
            //try again on error
            println!("trying again");
            a.clone().lock().await.do_something().await.unwrap();
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("begin");
    let a = Arc::new(Mutex::new(A{}));
    lock_and_use(a.clone()).await;
    println!("end");
    Ok(())
}

Playground

如果我这样做:

a.clone().lock().await.do_something().await;
a.clone().lock().await.do_something().await;

不会有问题,lock() 在它创建的同一行死掉。我认为这个原则对于 match 也是一样的。如果您考虑 match,它会锁定值,在其上调用 do_somethingawaits,然后比较值。确实do_somethingreturns一个future里面捕获了self,但是当我们await放在上面的时候,它应该丢弃self。为什么它仍然成立 self?如何在不克隆结果的情况下解决这个问题?

是:

Temporaries live for the entire statement, never shorter.

原因代码可能是:

{
    match self.cache.read() { // <-- direct pattern matching
        Ok(ref data) => Some(data)
        _ => None,
    }
}.map(|data| {
    // use `data` - the lock better be held
})

阅读 this issue 了解更多详情。

因此您需要在 match 语句之外锁定:

let x = a.clone().lock().await.do_something().await;
match x {
    Ok(()) => {}
    Err(()) => {
        a.clone().lock().await.do_something().await.unwrap();
    }
}