Rust循环声明中Mutex加锁后的锁释放时机问题
结论
你的预期不符合实际运行逻辑,示例代码中的互斥锁会被一直持有到整个for循环执行完毕,循环体内尝试重新获取该锁会直接触发死锁。
核心原因
核心是Rust临时值生命周期延长规则,与MutexGuard在drop时自动释放锁的机制共同作用的结果:
- 你的基础认知在普通表达式语句场景下是成立的:如果写独立语句
mtx.lock().await.some_method();,语句执行结束后,求值过程中产生的临时MutexGuard会被立刻丢弃,锁随即释放。 - 但for循环头属于Rust规定的临时值生命周期延长场景:
in关键字后整个表达式求值产生的所有临时值,生命周期都会被强制拉长到整个for循环执行结束,不会在clone()这类子表达式调用完成后立刻丢弃。 - 逐行拆解示例代码的实际运行逻辑:
- 首先执行
mtx.lock().await,生成临时MutexGuard实例,此时互斥锁被当前任务持有; - 接着通过
MutexGuard的自动解引用特性,调用内部被保护数据的clone()方法,生成一份和锁完全无关的可迭代克隆值; - 第一步生成的临时
MutexGuard不会在clone()返回后立刻drop,它作为for循环头表达式的关联临时值,会一直存活到整个循环所有迭代执行完成、退出循环作用域时才会被丢弃,锁到这时候才会真正释放。
- 首先执行
正确实现方式
如果需要拿到被保护数据的克隆值就立刻释放锁,让循环体内可以正常重新获取锁,需要把加锁、克隆的逻辑放到独立的短作用域里,主动限定MutexGuard的生命周期:
// 独立作用域中完成加锁、克隆,作用域结束时MutexGuard自动drop,锁立即释放 let iter_data = { let guard = mtx.lock().await; guard.clone() }; // 此时锁已经释放,遍历克隆数据时可安全重新获取锁 for ele in iter_data { // 业务逻辑,可正常调用mtx.lock().await,不会触发死锁 }
内容的提问来源于stack exchange,提问作者abcalphabet
相关产品推荐
相关产品推荐

