插入时同时迭代地图,这是不安全的

时间:2019-01-06 19:21:57

标签: c++ c++11 optimization visual-studio-2015 concurrency

注意:我知道这是不安全的,并且没有被标准定义,希望了解它是由我的编译器定义的,还是在实践中是安全的。

我正在一个线程中遍历一个映射范围,同时有可能插入另一个线程中

// thread 1:
for(auto it = map.begin(); it != map.end(); ++it){
    // it's okay if "it" is out of order, repeats an element, or skips an element
    // it's bad  if "it" can skip map.end() or turn to mush (invalid iterator)
}

// thread 2:
map[Key(...)] = Type(...); // insertions are extremely rare but inevitable

这是不安全的,但是...多么不安全?该映射用作缓解线程争用的优化提示,因此它本身无法促进该争用。如果可能的结果仅仅是插入的元素可能会丢失,或者它将读取乱序或两次的元素,那么这是可以接受的,不会破坏任何内容。

这样做会导致迭代器变成汤还是导致map.end()丢失?这是可能会破坏我生活的仅有的两个结果。

2 个答案:

答案 0 :(得分:6)

是的,这些结果绝对可能发生,甚至更糟

当您插入地图时,会发生各种内部操作。它不仅是[]=:下面还有整个算法,可能包括重新平衡操作! (有关更多信息,请参阅您关于树结构管理的旧大学笔记。)

在这样的算法过程中,无法保证在“观察”地图时会产生什么结果,该标准非常明确地表明您的程序将具有未定义的行为。这是一个两方面的问题:您不仅会在地图上遇到实际问题,而且编译器知道没有明确定义的程序可以进入这种情况,并且可以基于您所做的假设进行优化't 。现在,所有地狱都破灭了,potentially in code not even related to your map accesses

在一个线程中修改映射并在另一个线程中读取它而无需同步,这是自杀,简单明了的。不要试图超越编译器/实现的智慧:只需编写代码即可收缩。

答案 1 :(得分:-2)

我正在从臀部进行半拍,但可以说底层集合是通过预先分配固定大小来实现的,然后在需要增长时可能会分配例如多出50%以避免重复的小额分配。然后将旧的收集条目移到新分配的内存中,并释放旧的收集。

如果要在插入时迭代这样的集合,则“下一个”迭代器最终可能会持有指向释放位置的指针。