在插入时同时迭代一个映射,这在什么方面是不安全的

Concurrently iterating through a map while inserting, in what way is it unsafe

本文关键字:在什么 方面 不安全 映射 迭代 插入 一个      更新时间:2023-10-16

注意:我知道这是不安全的,并且是标准未定义的,我想看看它是由我的编译器定义的,还是在实践中是安全的。

我在一个线程中迭代映射范围,同时可能在另一个线程插入

// 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()丢失?这是仅有的两个可能毁掉我生活的结果。

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

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

无法保证在这种算法的过程中"观察"地图会产生什么结果,而且标准非常明确地表明,您的程序将具有未定义的行为。这是一个双重问题:你不仅会遇到映射的实际问题,而且你的编译器知道没有定义良好的程序可以进入这种情况,可以根据你没有的假设进行优化。现在,所有的地狱都松了,可能出现在与地图访问无关的代码中。

在一个线程中修改地图,在另一个线程读取地图,而不同步,这是自杀,简单明了。不要试图智胜编译器/实现:只需要契约代码即可。

我在这里半开玩笑,但假设底层集合是通过预先分配固定大小来实现的,然后当需要增长时,它可能会多分配50%,以避免重复的小规模重新分配。然后,旧集合条目将被移动到新分配的内存中,并且旧集合将被解除分配。

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