Rust中Dashmap调用remove死锁:为何使用owned值仍持有map引用
死锁原因解释
死锁的核心是Rust的临时对象生命周期规则,完全符合Dashmap文档提到的「持有map引用时调用remove会死锁」的约定:
DashMap的iter()方法返回的迭代器会持有对应数据分片的读锁,直到迭代器本身被销毁才会释放锁- Rust默认规则下,同一个语句内产生的所有临时对象,会等到整个语句完全执行完毕后才会统一销毁,不会在子表达式计算完成后提前销毁。
对应到你的代码:if let Some(i) = map.iter().next().map(|k| k.key().to_string()) 属于同一个表达式语句,map.iter()产生的迭代器是该语句的临时对象:
- 迭代器先拿到分片读锁,后续计算得到了owned类型的字符串
i - 但此时迭代器还没有被销毁,读锁仍然被持有
- 进入if代码块调用
map.remove(&i)时,remove需要获取同一分片的写锁,读写锁冲突直接导致死锁。
为什么提前存储变量就正常?
当你把迭代求值的逻辑单独放到独立的let语句时:
// 迭代逻辑单独作为一条语句 let opt_i = map.iter().next().map(|k| k.key().to_string()); if let Some(i) = opt_i { map.remove(&i); }
map.iter()产生的迭代器是let opt_i = ...这条语句的临时对象,该语句执行完成后迭代器就会被销毁,读锁随之释放,后续调用remove拿写锁不会有冲突,自然不会死锁。
你也可以通过手动加花括号限制临时对象生命周期来解决问题:
if let Some(i) = { // 迭代器生命周期被限制在花括号内,花括号执行完立刻销毁 map.iter().next().map(|k| k.key().to_string()) } { map.remove(&i); }
内容的提问来源于stack exchange,提问作者J. Doe
相关产品推荐
相关产品推荐

