首页 > 解决方案 > 保存对 tbb::concurrent_unordered_map 项目的引用是否安全?

问题描述

我遇到了一些我继承的现有代码,它查找并存储存储在 tbb::concurrent_unordered_map 中的结构的引用。我知道如果它是一个迭代器,它会是安全的,但对实际对象的引用似乎很可疑。

该代码不断将新项目插入 tbb::concurrent_unordered_map。插入是否可以不更改 tbb 映射中包含的项目的物理位置,这会使存储的引用指向错误的位置,就像 std::map 一样?

tbb 文档指出:

“像 std::list 一样,新项目的插入不会使任何迭代器无效,也不会更改映射中已有项目的顺序。插入和遍历可能是并发的。”

我知道对于 std::list 插入新项目时位置不会改变,但是因为 concurrent_unordered_map 文档只讨论了订单,我担心这并没有明确说明它可以移动位置。

下面是一些伪代码,演示了我遇到的代码的概念。

struct MyStruct {
    int i;
    int j;
};

//some other thread will insert items into myMap
tbb::concurrent_unordered_map <int, MyStruct> myMap;

MyStruct& getMyStruct (int id)
{
    auto itr=myMap.find (id);
    if (itr!=myMap.end ()) return itr->second;

    static MyStruct dummy {1,2};
    return dummy;
}


class MyClass {
public:
    MyClass (int id)
            :m_myStruct {getMyStruct (id)}
    {

    }

    void DoSomething () {
        std::cout<<m_myStruct.i<<std::endl;
    }
protected:
    MyStruct& m_myStruct; //reference to an item held into a tbb::concurrent_unordered_map
};

这段代码已经以相对较高的频率运行了一年多,这似乎表明它是安全的,但我想确定保持对 tbb::concurrent_unordered_map 中包含的项目的引用是安全的.

重写代码需要做很多工作,所以如果可以保持原样,我宁愿不必这样做。

谢谢,

保罗

标签: c++tbb

解决方案


如果您非常担心进入未完全记录的行为的灰色区域,为什么不存储和使用迭代器呢?一个问题是它的大小(运行它):

#include <tbb/tbb.h>

int main() {
    tbb::concurrent_unordered_map<int, int> m;
    printf("Size of iterator: %lu\n", sizeof(m.begin()));
}

这是因为除了指向元素的指针之外,迭代器还有指向它所属容器的指针。没有指针列表或任何其他结构来注册用户创建的所有迭代器,因此容器无法更新迭代器以指向不同的地址(这在并行程序中尤其棘手)。无论如何,这样的重定位会导致额外的用户可见调用键/值类型的移动/复制构造函数,因此将被记录。文档中没有这样的警告,添加它会导致向后不兼容的更改,这为您提供了一种间接保证,即您可以安全地保留指向项目的指针而不是使用迭代器。


推荐阅读