我正在寻找一个并发的关联容器,我从Thead Building Blocks中找到了concurrent_unordered_map
,这似乎符合我的所有需求。即使我阅读了文档,但我还没有找到一个关于擦除如何工作的例子。
A concurrent_unordered_map supports concurrent insertion and traversal, but not concurrent erasure. The interface has no visible locking. It may hold locks internally, but never while calling user-defined code. It has semantics similar to the C++11 std::unordered_map except as follows:
这究竟意味着什么?只要我从一个线程中删除就可以安全地从这个地图中删除它吗?如果没有,我该怎么做?
答案 0 :(得分:4)
proposal for standardization of concurrent maps解释了为什么并发容器没有erase
。 (搜索“为什么不支持并发擦除”部分。)
C ++标准库中的容器负责删除其内容。内容在容器中“,”就像元素“在”数组中一样。 operator[]
返回对包含对象的引用,一旦返回引用,容器就不知道请求线程将“挂起”到引用的时间长度。因此,如果另一个线程要求删除该元素,则容器不知道该元素是否可以安全删除。
我一直试图想出一些可以解决限制的方法,但是擦除问题的解释会带来更大的问题:总是来电者责任保护对存储在地图中的元素的并发访问。
例如:假设您有一个从int
到float
的并发地图,并执行此操作:
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));
(以上是错误的,因为它患有the ABA problem。)
即便如此:这仍然无法保护您免受对基础MyMapped_t
的同时修改,并要求您自己完成MyMapped_t
的所有构造,存储管理和销毁。
:(