代码如下。
意图是实现 no_lock 函数的带锁的版本。
dead_lock_1 函数 死锁了,我能想明白其中的临时 MutexGuard 有效期包括整个 while,导致内部拿不到锁。
dead_lock_2 函数 加个作用域,但还是死锁了。想不明白,临时变量能活过我新加的作用域?
no_dead_lock 函数 加个作用域,再写清楚变量。终于好了,这个是没有问题的。
结论就是:
{ vec_mutex.lock().unwrap().pop() } 是个块表达式, block expression, 虽然有个大括号,但不负责 drop 临时变量。
{ let mut vec = vec_mutex.lock().unwrap(); vec.pop() } 这里的临时变量是里面的语句, statement, 有分号的那个负责 drop 的。
use std::sync::Mutex;
fn main() {
dead_lock_2();
}
fn no_lock() {
let mut vec = vec![1,2,3];
while let Some(num) = vec.pop() {
if num == 2 {
vec.push(4);
}
println!("got {}", num);
}
}
fn dead_lock_1() {
let vec_mutex = Mutex::new(vec![1,2,3]);
// 会导致 MutexGuard 锁的有效期包括整个 while 导致死锁
while let Some(num) = vec_mutex.lock().unwrap().pop() {
if num == 2 {
vec_mutex.lock().unwrap().push(4);
}
println!("got {}", num);
}
}
fn dead_lock_2() {
let vec_mutex = Mutex::new(vec![1,2,3]);
// 这也会 为什么?
while let Some(num) = { vec_mutex.lock().unwrap().pop() } {
if num == 2 {
vec_mutex.lock().unwrap().push(4);
}
println!("got {}", num);
}
}
fn no_dead_lock() {
let vec_mutex = Mutex::new(vec![1,2,3]);
// 终于不会死锁了,但跟 dead_lock_2 有什么区别
while let Some(num) = {
let mut vec = vec_mutex.lock().unwrap();
vec.pop()
} {
if num == 2 {
vec_mutex.lock().unwrap().push(4);
}
println!("got {}", num);
}
}
1
共 9 条评论, 1 页
评论区
写评论我忽然发现这个理论说不通了
按上面这个说法,if里面也没有statement,所以下面的表达式应该panic……
但事实上这个表达式可以执行
--
👇
viruscamp: 我觉得这个能说通。
然后根据这个理论,构造出下面这种死锁。
--
👇
7sDream: 我可能懂了。
https://doc.rust-lang.org/stable/reference/expressions.html#temporaries
这里介绍了临时变量的 drop 时机:
这里的关键词是
statement
。在 https://doc.rust-lang.org/stable/reference/statements.html 这里可以看到,statement 只有五种
而
{ expr }
这种形式并不是一个 statement。根据 https://doc.rust-lang.org/stable/reference/expressions/block-expr.html,这其实是一个 Block expressions。
所以在第二种写法下,由于一直没有一个 statement 出现,那个
MutexGuard
的 drop 时机被放到了 while 语句结束时。而在后面那种作用于中有语句的情况下,就在当前那条语句的末尾了。
我觉得这个能说通。
然后根据这个理论,构造出下面这种死锁。
--
👇
7sDream: 我可能懂了。
https://doc.rust-lang.org/stable/reference/expressions.html#temporaries
这里介绍了临时变量的 drop 时机:
这里的关键词是
statement
。在 https://doc.rust-lang.org/stable/reference/statements.html 这里可以看到,statement 只有五种
而
{ expr }
这种形式并不是一个 statement。根据 https://doc.rust-lang.org/stable/reference/expressions/block-expr.html,这其实是一个 Block expressions。
所以在第二种写法下,由于一直没有一个 statement 出现,那个
MutexGuard
的 drop 时机被放到了 while 语句结束时。而在后面那种作用于中有语句的情况下,就在当前那条语句的末尾了。
我可能懂了。
https://doc.rust-lang.org/stable/reference/expressions.html#temporaries
这里介绍了临时变量的 drop 时机:
这里的关键词是
statement
。在 https://doc.rust-lang.org/stable/reference/statements.html 这里可以看到,statement 只有五种
而
{ expr }
这种形式并不是一个 statement。根据 https://doc.rust-lang.org/stable/reference/expressions/block-expr.html,这其实是一个 Block expressions。
所以在第二种写法下,由于一直没有一个 statement 出现,那个
MutexGuard
的 drop 时机被放到了 while 语句结束时。而在后面那种作用于中有语句的情况下,就在当前那条语句的末尾了。
还是用之前那个 mock 代码,这样的话输出是:
所以这样的话是在用完之后立刻就 drop 了,甚至没有等到作用域结束。
更奇妙了感觉。
挺有趣的一个问题,确实感觉
dead_lock_2
这种在 while 条件里加了作用域的情况下不应该死锁的。只可能是
MutexGuard
并没有在那个作用域结束时立即被drop
了。写了个代码验证下:
输出结果:
结果是
MockGuard dropped
在got n
之后才会出现,也就是一轮 loop 结束后才会被drop
。原因是知道了,至于为啥是这样,确实比较迷惑。
个人见解,no_dead_lock()函数中大括号里的let表达式会引入一个新的作用域, vec获得锁后在第一层大括号的作用域里,但是经过let表达式后,返回给num的值是vec转移到let表达式产生的隐式的作用域中pop出来的值,返回后vec这个变量实际上是被drop掉的。
或许可以这样:
原理仍然是释放临时变量
我只知道这样也不会死锁
大概是临时变量在语句结束之后才
drop
的缘故.lock
生成的临时变量在语句(一次while
循环)结束之后才drop
,这导致了死锁。加分号的意思大概是让临时变量在分号之后直接
drop
,这样可以将解决死锁问题。一个能解决问题的新写法,但更难解释。