使用并行线程填充集合有啥危险吗?

Posted

技术标签:

【中文标题】使用并行线程填充集合有啥危险吗?【英文标题】:Is there any danger in populating a set using parallel threads?使用并行线程填充集合有什么危险吗? 【发布时间】:2017-11-29 01:00:36 【问题描述】:

我可以使用 C++ 中的并行线程填充 std::set 对象并编辑布尔向量吗?

我对并行计算比较陌生,刚刚学习使用 OpenMP,我听说有人说线程之间可以共享数据结构,只要它们是只读的。

我的问题是,我可以使用并行方法填充一些数据结构,只要顺序无关紧要吗?还是您通常不做类似事情的原因不仅仅是订单不会被保留?

这是我想要做的:

input: std::vector<Object> u_set // vector containing universal set

int NUM_ELEMENTS = 1000;
std::set<int> my_set(); // set to be populated
std::vector<bool> my_bools(u_set.size(), false); // vector containing set membership information (true/false)
#pragma omp parallel for
for (int i = 0; i < NUM_ELEMENTS; ++i)
    int next_el = get_next_element(); // next element from u_set
    my_set.insert(next_el);
    my_bools[next_el] = true;

代码基本上从通用集中选择一个元素,然后将其值添加到子集中,并将其标记在布尔向量中作为子集的成员。可以并行执行此操作吗?还是被认为是不好的形式?这样的东西会更好吗?

input: std::vector<Object> u_set // vector containing universal set

int NUM_ELEMENTS = 1000;
std::set<int> my_set(); // set to be populated
std::vector<bool> my_bools(u_set.size(), false); // vector containing set membership information (true/false)

int max_threads = omp_get_max_threads();
std::vector<std::vector<int> > elements(max_threads); // vector to contain data from each thread, so not accessing the same data structure

#pragma omp parallel for
for (int i = 0; i < NUM_ELEMENTS; ++i)
    int next_el = get_next_element(); // next element from u_set
    int curr_thread = omp_get_thread_num();
    elements[curr_thread].push_back(next_el);


for (auto it = elements.begin(); it != elements.end(); ++it)
    for (auto jt = elements[*it].begin(); jt != elements[*it].end(); ++it)    
    my_set.insert(*jt);
    my_bools[*jt] = true;
    
 

这会为每个线程创建一个单独的向量,然后在最后将它们组合起来。我知道它在技术上仍然在访问相同的数据结构,但在我看来,将向量在整个数据结构中分开会使其免受混淆的额外安全级别。

这是更好的方法吗?还是只访问同一个集合并随着算法的进行添加就可以了?

【问题讨论】:

简短的回答是:不。 std::set 不是线程安全的。 只有const成员函数可以被并发访问。见en.cppreference.com/w/cpp/container#Thread_safety 很公平,您有什么建议可以并行化此代码吗?第二种方式安全吗?因为它没有在相同的向量上调用 push_back? 【参考方案1】:

当任何其他线程调用任何其他方法时,您不能调用std 容器的几乎所有非const 方法。 (begin()end() 和其他一些是例外)。

所以没有。

其他并行库中使用的一种技术是,每个线程将更改累积在一个子容器中,工作线程以类似二叉树的方式自动将其合并到最终结果中。对于您使用的工具,这似乎不太合理。

【讨论】:

那么即使是第二种方式也行不通?为每个线程保留一个单独的向量?

以上是关于使用并行线程填充集合有啥危险吗?的主要内容,如果未能解决你的问题,请参考以下文章

视图可以与并行集合一起使用吗?

如何设置并行集合的线程号?

Spark 中的并行集合

对集合进行并行计算方法选择的建议:stream的并行流,CompletableFuture

遍历 Java 集合以使这些球反弹,有啥提示吗?

在 OpenMP 并行代码中,memset 并行运行有啥好处吗?