我有一个使用很多std::map结构的程序。现在,我想将它们与多个线程一起使用,并假定插入或删除键可能会更改整个数据结构并并行破坏它。但是,当我不添加新 key 时,应该没问题吧?

以下程序显示了我想做什么:

#include <omp.h>

#include <iostream>
#include <map>

int main(int const argc, char const *const *const argv) {
  // Take a map and allocate the elements, but not fill them at this point.
  std::map<int, int> map;
  int size = 10000;
  for (int i = 0; i < size; ++i) {
    map[i];
  }

  // Go through the elements in parallel and write to them, but not create any
  // new elements. Therefore there should not be any allocations and it should
  // be thread-safe.
#pragma omp parallel
  {
    int const me = omp_get_thread_num();
#pragma omp for
    for (int i = 0; i < size; ++i) {
      map[i] = me;
    }
  }

  // Now all threads access all the elements of the map, but as the map is not
  // changed any more, nothing bad should happen.
#pragma omp parallel
  {
    int const me = omp_get_thread_num();
    int self = 0;

    for (int i = 0; i < size; ++i) {
      if (map[i] == me) {
        ++self;
      }
    }

#pragma omp critical(cout)
    std::cout << "Thread " << me << " found " << self << " entries.\n";
  }
}

然后用以下代码进行编译:
$ g++ -fopenmp -O3 -Wall -Wpedantic -g -fsanitize=address -o concurrent-map concurrent-map.cpp

这似乎与四个线程一起正常工作。如果我注释掉第一个for循环并让线程填充映射,则它会由于我期望的段错误而崩溃。

当然,我不能以这种方式证明std::map是线程安全的,但至少不能证明它是负面的。我可以并行使用std::map吗?

最佳答案

我认为使用map[i]并不是对所有C++实现都是线程安全的,即使它没有插入新元素也是如此。该标准不要求operator[]不受关联容器的数据竞争的影响:

C++ 17标准草案的[container.requirement.dataraces]/1节包含一个不引起数据争用的函数列表,即使它们不是const。该列表包括findat,但不包括operator[]

因此,您需要使用findat而不是operator[]。特定的实现可能会提供更强的保证,并且如果map[i]没有插入新元素,则可能会实现,但是您需要与编译器/标准库文档一起检查这一点。

除此之外,访问,甚至修改容器的其他元素总是可以的(vector<bool>除外),请参阅标准的下一段。

关于c++ - 当我不添加/删除键时,可以并行使用std::map吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58692995/

10-11 22:41
查看更多