从并发关联容器中删除(concurrent_unordered_map)

时间:2013-05-25 07:37:33

标签: c++ multithreading concurrency tbb

我正在寻找一个并发的关联容器,我从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:

这究竟意味着什么?只要我从一个线程中删除就可以安全地从这个地图中删除它吗?如果没有,我该怎么做?

1 个答案:

答案 0 :(得分:4)

proposal for standardization of concurrent maps解释了为什么并发容器没有erase。 (搜索“为什么不支持并发擦除”部分。)

C ++标准库中的容器负责删除其内容。内容在容器中“,”就像元素“在”数组中一样。 operator[]返回对包含对象的引用,一旦返回引用,容器就不知道请求线程将“挂起”到引用的时间长度。因此,如果另一个线程要求删除该元素,则容器不知道该元素是否可以安全删除。

我一直试图想出一些可以解决限制的方法,但是擦除问题的解释会带来更大的问题:总是来电者责任保护对存储在地图中的元素的并发访问。

例如:假设您有一个从intfloat的并发地图,并执行此操作:

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的所有构造,存储管理和销毁。

:(