You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何在函数参数中处理Mutex解锁会导致程序永久阻塞?

为何在函数参数中处理Mutex解锁会导致程序永久阻塞?

这问题挺典型的,咱们一步步拆解来搞明白为啥会这样:

首先看第一个能正常跑完的代码:

use std::sync::Mutex;

fn f(_: usize, _: usize) {}

fn main() {
    let m = Mutex::new(0);

    let i = { *m.lock().unwrap() };
    let j = { *m.lock().unwrap() };
    f(i, j);
}

这里的花括号是独立的小代码块,m.lock().unwrap()返回的MutexGuard(锁的守卫)在代码块结束后就会自动被销毁——因为咱们只是把它解引用后的值存到了i里,代码块执行完,守卫就离开了自己的作用域,锁也就跟着释放了。等咱们去获取j的时候,锁已经是空闲状态了,自然不会有问题。

再看第二个同样能正常运行的代码:

use std::sync::Mutex;

fn f(_: usize, _: usize) {}

fn main() {
    let m = Mutex::new(0);

    f(
        {
            let i = *m.lock().unwrap();
            i
        },
        {
            let j = *m.lock().unwrap();
            j
        },
    );
}

每个参数里的花括号也是独立的作用域。第一个代码块执行时,获取锁、解引用拿到值存到i,代码块一结束,守卫就被销毁,锁释放。之后才会执行第二个代码块的逻辑,这时候再拿锁完全没问题,函数调用自然能顺利完成。

重点来了,看这个会永久阻塞的代码:

use std::sync::Mutex;

fn f(_: usize, _: usize) {}

fn main() {
    let m = Mutex::new(0);

    f({ *m.lock().unwrap() }, { *m.lock().unwrap() });
}

这里的核心坑点是Rust中函数参数临时变量的存活时间规则:函数调用时,所有参数的临时值(包括这里的MutexGuard),都会存活到整个函数调用完成之后才会被销毁。

也就是说,第一个参数里的{ *m.lock().unwrap() },虽然你解引用取了数值,但生成的MutexGuard并不会在花括号结束后就释放——它作为函数参数的临时依赖,要等到f()整个调用彻底结束才会被drop掉。

那流程就变成了:先求值第一个参数,获取锁,守卫一直活着;然后求值第二个参数,又去尝试获取同一个Mutex的锁,这时候第一个锁还死死攥着没释放,直接就造成了死锁,程序自然就永久阻塞住了。

简单总结:前两个例子里,我们用独立代码块限制了MutexGuard的作用域,代码块结束锁就释放;第三个例子里,守卫的存活时间被延长到了整个函数调用期间,导致第二个参数拿锁时第一个锁还没松,最终死锁。

备注:内容来源于stack exchange,提问作者ynn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 08:43:06