在结构体中传递 Mutex 和 MutexGuard

por*_*ton 6 mutex lifetime rust automatic-ref-counting

我试图返回一个包含对共享互斥体的引用的结构:

struct Test<'a> {
    mutex: Arc<Mutex<()>>,
    guard: &'a MutexGuard<'a, ()>,
}

impl<'a> Test<'a> {
    pub fn new() -> Self {
        let mutex = Arc::new(Mutex::new(()));
        let guard = &mutex.lock().unwrap();
        Self {
            mutex,
            guard,
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

生命周期似乎是正确的:互斥体至少在 的生命周期内存在Test,因此MutexGuard没有对互斥体的停滞引用。但 Rust 给出了错误。如何向 Rust 解释字段的生命周期mutex足够长,可以guard正常工作?

cannot return value referencing local variable `mutex`
returns a value referencing data owned by the current function
Run Code Online (Sandbox Code Playgroud)

顺便说一句,我正在尝试创建一个“mutli-mutex” - 一组键的互斥体(如 中所示HashMap),以阻止下载名称在 hashmap 中的文件(因为它已经在下载)。

And*_*rea 0

这是不可能的,而且你写的很可能不是你想要做的。

互斥锁防护的目标是在互斥锁掉落时将其解锁。如果将守卫的生命周期绑定到互斥体本身,则表示它永远不会被删除,因此互斥体将始终被锁定。

不管怎样,为什么你需要引用守卫本身?通常您不会关心,您只是希望它保持锁定,只要它被引用即可。

也许你想这样做?人们需要更多的背景来看看这是否正是您想要做的:D

use std::sync::{Arc, Mutex, MutexGuard};

struct Test {
    mutex: Arc<Mutex<()>>,
}

impl Test {
    pub fn new() -> Self {
        let mutex = Arc::new(Mutex::new(()));
        Self { mutex }
    }

    pub fn try_download(&self) -> Option<MutexGuard<()>> {
        let guard = self.mutex.try_lock();

        if guard.is_ok() {
            println!("Download started!");
            return guard.ok();
        } else {
            println!("Cannot download since it's already downloading");
            return None;
        }
    }
}

fn main() {
    let test = Test::new();

    // This could be kept alive like you said in an hashmap, so the guard is not dropped
    //  v
    let a = test.try_download(); // Download started!
    let b = test.try_download(); // Cannot download since it's already downloading
}
Run Code Online (Sandbox Code Playgroud)

为什么你的代码不起作用

实际上有2个问题

  1. 引用未知的堆栈空间(段错误!)
impl<'a> Test<'a> {
    pub fn new() -> Self {
        let mutex = Arc::new(Mutex::new(()));
        //    ^
        //  This reference has the lifetime of mutex
        //          v
        let guard = &mutex.lock().unwrap(); 

        Self {
            mutex, // mutex is moved here, so guard points to something which is at this time
                   // unknown. This can be known only after the new stack frame is built
            guard,
        }
    }
}

Run Code Online (Sandbox Code Playgroud)
  1. 互斥体真正存储在哪里?在您的示例中,它位于 new 函数的堆栈帧上。然后,您将指向它的指针存储在结构中并返回它。通过返回,你也会删除堆栈,那么指针指向哪里呢?(段错误!)
impl<'a> Test<'a> {
    pub fn new() -> Self {
        let mutex = Arc::new(Mutex::new(()));
        //    ^
        //  This reference has the lifetime of mutex
        //          v
        let guard = &mutex.lock().unwrap(); 

        Self {
            mutex, // mutex is moved here, so guard points to something which is at this time
                   // unknown. This can be known only after the new stack frame is built
            guard,
        }
    }
}

Run Code Online (Sandbox Code Playgroud)