并发映射标准化的提议解释了为什么并发容器没有erase
. (搜索“为什么不支持并发擦除”部分。)
C++ 标准库中的容器负责删除它们的内容。内容“在”容器中,就像一个元素“在”一个数组中一样。 operator[]
返回对包含对象的引用,一旦返回引用,容器就不知道请求线程将“挂起”引用多长时间。因此,如果另一个线程出现要求删除该元素,则容器不知道该元素是否可以安全删除。
我一直在想一些方法可以解决这个限制,但是对擦除问题的解释带来了一个更大的问题:保护对存储在地图中的元素的并发访问始终是调用者的责任。
例如:假设您有一个并发映射 from int
tofloat
并且您这样做:
thread A thread B
the_map[99] = 0.0;
...
the_map[99] = 1.0; if (the_map[99] == 1.0) ... // data race!!!
这是数据竞争的原因是即使表达式the_map[99]
受到保护,它也会返回一个引用,对它的访问不受保护。由于对引用的访问不受保护,因此只允许读取引用指向的内存。(或者您需要锁定对该内存的所有访问)。
这是我能想到的最便宜的选择(而且真的很贵):
typedef concurrent_unordered_map<MyKey_t, atomic<MyMapped_t*> > MyContainer_t;
现在查找一个项目意味着:
MyMapped_t* x = the_map[a_key].load();
插入一个项目是:
the_map[a_key].store(ptr_to_new_value);
擦除项目是:
the_map[a_key].store(0);
并测试一个项目是否真的在地图中是:
if (the_map[a_key].load() != 0) ...
最后,如果您要进行任何类型的有条件擦除(或修改),它必须比以下内容更复杂:
MyMapped_t* x;
do {
x = the_map[a_key].load();
} while (condition_for_erasing(x) && !the_map[a_key].compare_exchange_strong(x, 0));
(上面是错误的,因为它存在 ABA 问题。)
即便如此:这仍然不能保护您免受对底层的同时修改,MyMapped_t
并且需要您自己完成所有的构建、存储管理和销毁MyMapped_t
。
:(