C++ OpenMP:并发写入std::map

C++ OpenMP:并发写入std::map,c++,c++11,openmp,C++,C++11,Openmp,我有一个std::map,其键与线程数相同。每个线程都在写入值,这里是一个std::vector。因此,可以保证每个线程只在“his”std::vector上运行 例如: #include <iostream> #include <omp.h> #include <map> #include <vector> int main(void) { std::map<unsigned int, std::vector<unsig

我有一个
std::map
,其键与线程数相同。每个线程都在写入值,这里是一个
std::vector
。因此,可以保证每个线程只在“his”
std::vector
上运行

例如:

#include <iostream>
#include <omp.h>
#include <map>
#include <vector>


int main(void) {

    std::map<unsigned int, std::vector<unsigned int> > M;

// initialize map using first touch policy:
#pragma omp parallel for schedule(static,1)
    for (int i=0; i<omp_get_num_procs(); ++i) {
#pragma omp critical
        M[omp_get_thread_num()] = std::vector<unsigned int>();
    }

// do some parallel operations:
#pragma omp parallel for schedule(static)
    for (int i=0; i<100; ++i) {
        M[omp_get_thread_num()].push_back(i);
    }

// disp the content:
    for (auto it_key : M) {
        std::cout << "thread " << it_key.first << " : {";
        for (auto it_vec : it_key.second) {
            std::cout << it_vec << " ";
        }
        std::cout << "\b \b}" << std::endl;
    }

    return 0;
}
#包括
#包括
#包括
#包括
内部主(空){
std::map M;
//使用首次触摸策略初始化地图:
#计划的pragma omp并行(静态,1)

对于(inti=0;i您的第二个循环看起来很好,因为您没有修改映射本身,而是只修改键下的值。 从

数据竞赛 容器被访问,并可能被修改。 函数访问一个元素并返回一个可用于修改其映射值的引用。同时访问其他元素是安全的。 如果函数插入新元素,则在容器中同时迭代范围是不安全的

第一个循环最好不要同时进行,因为不管怎么说,这是最小的工作量-在一个线程中进行,然后在syn_线程中进行。按照标准,现在的方式似乎不安全,只是碰巧在您的配置上工作

编辑


实际上,由于在第一个循环中,您不访问其他元素,而只是插入新的元素,因此根据标准,它也是安全的,尽管同时执行它没有任何好处。

我不理解您的第一个循环,并行运行它是没有意义的,因为映射将以排序顺序存储所有内容。除此之外,您的第二个循环在我看来是合法的。@GuyGreer好吧,第一个循环可能是不必要的复杂。这个想法是在向量上运行的线程应该自己创建它(ccNUMA Location,first touch)。重要的是,必须事先初始化密钥。@Ilya Kobelevskiy:谢谢。关键问题是映射的密钥是非并发初始化的(或使用critical),因为密钥在第一次使用时会自动进行静默初始化。我在并行区域中进行初始化,因为分配的内存映射到首先接触它的内核的域(ccNUMA)。但事实上,没有必要这样做。@U ducky Right,我忽略了第一个循环不是并发的,因为omp-critical。根据对映射文档的引用,即使没有omp-critical,它也应该可以工作,因为无法访问该循环中涉及的其他元素/迭代。@Ilya Kobelevskiy:不,省略时它不起作用g omp是关键的。初始化密钥的非并发是绝对必要的,因为否则多个线程可能会试图同时插入到映射中。这会导致未定义的行为和错误。