在单个表达式中对同一变量的第二次锁定无限期地阻塞

Second lock on same variable in a single expression blocks indefinitely

我有一个 Node 包含共享 Protocol 上的 Mutex,它又在线程池中的不同线程之间使用:

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

pub struct Node {
    thread_pool: ThreadPool,
    protocol: Arc<Mutex<Protocol>>,
}

pub struct Protocol {}
impl Protocol {
    pub fn is_leader(&self) -> bool {
        // Do stuff...
    }

    pub fn is_co_leader(&self) -> bool {
        // Do stuff...
    }
}

当我尝试在同一 if 语句中获取 Nodeprotocol 上的锁时,该语句中的代码永远不会执行。

impl Node {
    pub fn sign(&mut self) {
        let protocol_handler = Arc::clone(&self.protocol);

        self.thread_pool.execute(move || {
            if !protocol_handler.lock().unwrap().is_leader()
                && !protocol_handler.lock().unwrap().is_co_leader()
            {
                // This is never executed
            }

            // And this neither...
        })
    }
}

但是,如果将方法调用的值分配给两个变量,一切都会按预期进行:

impl Node {
    pub fn sign(&mut self) {
        let protocol_handler = Arc::clone(&self.protocol);

        self.thread_pool.execute(move || {
            let is_leader = protocol_handler.lock().unwrap().is_leader();
            let is_co_leader = protocol_handler.lock().unwrap().is_co_leader();

            if !is_leader && !is_co_leader {
                // Either this will be executed
            }

            // or this ...
        })
    }
}

在第一种情况下,Rust 的行为无限期等待是否有任何具体原因?

这是针对您的问题的 MCVE:

use std::sync::Mutex;

fn main() {
    let foo = Mutex::new(42i32);

    let f1 = (*foo.lock().unwrap()).count_ones();
    println!("f1: {}", f1);
    let f2 = (*foo.lock().unwrap()).count_zeros();
    println!("f2: {}", f2);

    let tot = (*foo.lock().unwrap()).count_ones() + (*foo.lock().unwrap()).count_zeros();
    println!("tot: {}", tot);
}

playground

当 运行 此代码将打印 f1f2,然后在尝试计算 tot.

时挂起

问题是 Mutex::lock returns 一个 MutexGuard 会在超出范围时自动释放锁。在上面的示例中,守卫在使用它们的表达式末尾超出范围。所以当我写:

let f1 = (*foo.lock().unwrap()).count_ones();

我获取了锁,读取了值,然后释放了锁。因此在计算 f2.

时锁是空闲的

然而,当我写:

let tot = (*foo.lock().unwrap()).count_ones() + (*foo.lock().unwrap()).count_zeros();

我获取了锁,读取了值,尝试再次获取锁并且只在行尾释放了两个守卫。当我在没有先释放锁的情况下第二次尝试获取锁时,这会导致代码死锁。

请注意,正如 trentcl 所评论的那样,如果互斥体被锁定的两次之间发生了变化,则您的两步示例会受到竞争条件的影响。你应该使用这样的东西:

impl Node {
    pub fn sign(&mut self) {
        let protocol_handler = Arc::clone(&self.protocol);

        self.thread_pool.execute(move || {
            let handler = protocol_handler.lock().unwrap();

            if !handler.is_leader && !handler.is_co_leader {
                // Either this will be executed
            }

            // or this ...
        })
    }
}