使用 Rust Dashmap 这些代码会产生死锁吗?

Will these code produce a deadlock using Rust Dashmap?

在 Rust 中使用 DashMap 这样的代码会产生死锁吗?

// snippet_1
let a = DashMap::new();
let b = DashMap::new();

// thread1
for v in a.iter(){
   xxx
}
for v in b.iter(){
   xxx
}

//thread2
for v in b.iter(){
   xxx
}
for v in a.iter(){
   xxx
}
// snippet_2
let a = DashMap::new();
let b = DashMap::new();

// thread1
for v in a.iter(){
   xxx
}
for v in b.iter(){
   xxx
}

//thread2
for v in b.iter(){
   xxx
   for v in a.iter() {
      xxx
   }
   xxx
}
// snippet_3
let a = DashMap::new();
let b = DashMap::new();

// thread1
for v in a.iter(){
   xxx
}
for v in b.iter(){
   xxx
}

//thread2
for v in b.iter(){
   xxx
   let Some(v) = a.get_mut(key){
      xxx
   }
   xxx
}

此外,在同一个线程中迭代 dashmap 时插入它会产生死锁。但是,从另一个线程插入 dashmap 不会产生死锁。是真的吗?

None 个示例会产生死锁。

来自文档:

insert():向映射中插入一个键和一个值。 Returns 与密钥关联的旧值(如果有的话)。
锁定行为:如果在映射中持有任何类型的引用时调用可能会死锁。

iter():在 DashMap 上创建一个迭代器,产生不可变引用。
锁定行为:如果在映射中持有可变引用时调用,可能会死锁。

get_mut(): 获取映射中条目的可变引用
锁定行为:如果在映射中持有任何类型的引用时调用可能会死锁。

解释:

在您的前两个示例中,仅使用了 iter(),因此只要不使用其他将可变引用引入映射的操作,就不会出现死锁。

在您的第三个示例中,DashMap b 仅再次与 iter() 一起使用,因此那里没有死锁。对于 DashMap a 有两种可能的执行流程:

  1. 如果线程 1 首先到达 a.iter(),则线程 2 将不得不在 a.get_mut(key) 处等待,但是一旦线程 1 完成对 a 的迭代,线程 2 将能够继续,所以不会有死锁。
  2. 如果线程 2 首先到达 a.get_mut(key),则线程 1 将不得不在 a.iter() 处等待,但是一旦线程 2 完成,它将能够继续并且不会出现死锁。

其他问题:

在同一线程中迭代 DashMap 时插入 DashMap 会产生死锁。

例子:

for v in a.iter() { // This takes a reference into `a`
    a.insert(...) // Will deadlock because you hold a reference into `a`
}

从一个线程插入 DashMap 而在另一个线程迭代 DashMap 不会产生死锁,其中一个只会等待另一个。

例子:

//thread1
for v in a.iter(){ //If thread2 reaches `insert()` first, this will wait until it has finished.
    xxx
}

//thread2
for i in 1..1000 {
    a.insert(i, i); // If thread1 reaches `iter()` first, this will wait until it has finished.
}

重要的是要注意,在问题的最后一个示例和第三个示例中,insert()iter() 可能不会等待整个循环完成才开始,而是执行两个循环中的一个将交错,但它们永远不会同时执行。