为什么Rust中的锁会在整个`if let`代码块内保持持有状态?
为什么Rust中的锁会在整个
if let代码块内保持持有状态? 嘿,这个坑我刚摸Rust的时候实打实踩过,简直给自己整懵了!其实核心原因就在于Rust锁的RAII设计和if let的生命周期规则。
先给你掰扯清楚:
- Rust标准库的
Mutex(还有RwLock)调用lock()后,返回的是个叫MutexGuard的智能指针——这玩意儿是RAII风格的守卫,简单说就是:它活着的时候,锁就被你攥在手里;等它被销毁(也就是Rust自动调用drop方法)的时候,锁才会被释放。 - 那
if let这里为啥锁会握全程?因为你在if let的条件里直接调用了number.lock().unwrap(),这个MutexGuard的生命周期会被自动延长到整个if let代码块结束!也就是说,从你拿到锁开始,直到if let的大括号}闭合,这个守卫都不会被销毁,锁自然也就一直没释放。
就拿你给的代码举例子:
use std::sync::Mutex; fn main() { let number = Mutex::new(Some(1)); if let Some(i) = number.lock().unwrap().take() { // 这里拿到了锁,守卫诞生 println!("{}", i); assert!(number.lock().unwrap().is_none()); // 这里又要拿锁,死锁! } // 到这里,守卫才被销毁,锁才释放 }
你看,在if let代码块里第二次调用lock()的时候,同一个线程已经握着这把锁了,但标准库的Mutex根本不支持递归锁——也就是同一个线程不能重复拿已经持有的锁,这就直接把自己卡死了,等着释放锁但锁在自己手里还没放,可不就死锁了嘛。
而且不止Mutex,RwLock也是一模一样的道理,不管是读锁还是写锁,只要是RAII的守卫,在if let条件里创建的话,都会赖在整个代码块里不走,锁也就一直被占着。
那怎么改?很简单,要是你不需要锁在整个代码块都持有,就把锁的操作移到if let外面,让守卫提前销毁:
use std::sync::Mutex; fn main() { let number = Mutex::new(Some(1)); // 先拿锁、取数据,这行代码结束后,守卫就被销毁,锁释放了 let maybe_i = number.lock().unwrap().take(); if let Some(i) = maybe_i { println!("{}", i); // 这里再拿锁完全没问题,因为锁已经释放了 assert!(number.lock().unwrap().is_none()); } }
内容来源于stack exchange
相关产品推荐
相关产品推荐

