【问题标题】:Possible thread unsafe operation on std::mapstd::map 上可能的线程不安全操作
【发布时间】:2015-12-15 13:14:45
【问题描述】:

STL 不保证其集合的线程安全。但我想知道以下代码是否真的有效。

一个线程通过调用 [] 运算符在映射 m 上调用非常量操作,但使用映射上已经存在的键。据我所知,这只是调用 find() 并返回对 gcc 中迭代器的引用。另一个线程同时持有 m 的 const 迭代器。

问题是:断言会失败吗?

void doBracket(std::map<int, int>& m) {
  const auto&  val = m[0];
  std::cerr << val << std::endl;
}


void doIter(const std::map<int, int>& m){
  auto zeroIter = m.find(0);
  auto oneIter = m.find(1);
  auto twoIter = m.find(2);
  assert(zeroIter->second == 0);
  assert(oneIter->second == 1);
  assert(twoIter->second == 2);
}

int main() {
  std::map<int, int> m = {{0, 0}, {1, 1}, {2, 2}};
  std::thread mutateThread {doBracket, std::ref(m)};
  std::thread constThread  {doIter, std:ref(m)};
  mutateThread.join();
  constThread.join();
}

这是 stl_map 的作用:

   operator[](const key_type& __k)
      {
        // concept requirements
        __glibcxx_function_requires(_DefaultConstructibleConcept<mapped_type>)

        iterator __i = lower_bound(__k);
        // __i->first is greater than or equivalent to __k.
        if (__i == end() || key_comp()(__k, (*__i).first))
                 //handle this case

        return (*__i).second;
      }

【问题讨论】:

  • 无论如何,您的代码都不是线程安全的。每个 ìtererator 更改操作都必须由互斥锁保护。
  • Dieter:我知道标准是这么说的。我对实际的实现更感兴趣。我更新了 stl_map 代码,这似乎暗示该代码实际上是线程安全的。

标签: c++ stl thread-safety


【解决方案1】:

如果你查看http://www.cplusplus.com/reference/map/map/operator%5B%5D/,它会说关于 map::operator[]:

迭代器有效性

没有变化。

数据竞赛

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

根据这一点,唯一可能不安全的情况是“如果函数插入新元素,则同时迭代容器中的范围”。由于您没有进行任何插入,因此结论一定是您的代码是安全的。

【讨论】:

    【解决方案2】:

    您似乎已经确定了一个可以通过了解实现来超越标准的地方。但是,这种方法与使用find() 而不是operator[]() 的保证安全方法相比没有优势。

    侧边栏:我个人觉得std::map::operator[]() 几乎没用。我总是想使用find()insert() 或其他东西。

    【讨论】:

    • 谢谢,约翰。实际上,我正在调试一种情况,我们恰好遇到这种情况(有两个线程,一个在映射上迭代,一个在已知键上执行括号运算符)。该地图似乎已损坏,并且认为可能是非常量括号运算符导致损坏。从实现来看,情况似乎并非如此。我同意应该将代码更改为使用 find()。但我想排除这种可能性。
    • 嗯,你应该把代码改成使用find(),看看能不能解决问题。如果您使用的是 Linux,您还应该考虑使用 valgrind 和 helgrind 等工具来加快故障排除速度。
    猜你喜欢
    • 2010-12-23
    • 2011-01-11
    • 2013-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 1970-01-01
    • 2020-08-24
    相关资源
    最近更新 更多