我有一个Node
,它在共享的Mutex
上包含Protocol
,而该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...
}
}
又在线程池中的不同线程之间使用:
protocol
当我尝试在相同的Node
语句中获取if
的{{1}}的锁时,该语句中的代码将永远不会执行。
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的行为无限期等待?
答案 0 :(得分:2)
以下是您遇到的问题的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);
}
运行此代码时,它将打印f1
和f2
,然后在尝试计算tot
时挂起。
问题是Mutex::lock
返回一个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 ...
})
}
}