【问题标题】:std::map thread-safety and iterator invalidationstd::map 线程安全和迭代器失效
【发布时间】:2018-04-07 08:00:13
【问题描述】:

我正在尝试确定以下示例代码是否是线程安全的:

std::map<K, V> myMap;

void foo() {
   myMap[k]; // Construct a new key-value pair with key `k`.
}

void bar() {
   auto it = myMap.find(x);
   if (it != myMap.end()) {
      std::lock_gaurd<std::mutex> lg(...);
      // do something with *it
   }
}

没有发生删除(至少在程序终止之前),唯一的操作是在foo 中添加元素并在bar 中迭代它们。函数foo 将在一个线程中调用,bar 将在多个线程中同时调用。

现在,我了解到 STL 容器不是线程安全的,我正在使用 myMap::operator[] 同时执行非常量操作。我感到困惑的是,因为 STL find 不会更改底层映射,并且因为 myMap::operator[] 不会使迭代器或引用无效,所以这仍然被认为是不安全的吗?我的想法有点冲突:我应该担心因为operator[] 是非常量,因此我正在对数据结构进行并发更改,还是我可以认为这是安全的,因为它不会使我的迭代器失效?

【问题讨论】:

  • 不变量在调用非常量函数之前和之后都有效,但不保证在运行期间都有效。

标签: c++ multithreading stl


【解决方案1】:

你的怀疑是对的。不能同时从不同线程调用myMap[k]myMap.find(x)

使用容器,您可以一次安全地执行以下操作之一:

  1. 同时从任意个线程调用 const 成员函数,或者

  2. 仅从一个线程调用非常量成员函数。

由于operator[] 不是const,您必须使用互斥锁来保护它,否则请确保您不会同时调用任何其他成员函数。

【讨论】:

  • 一个补充:仅在foo() 中添加lock_guard 是不够的,因为bar() 锁定为时已晚,如果foo() 并发执行。例如:foo() 可以持有锁,bar() 可能仍然执行.find(),然后foo() 会在映射中插入一些东西——使itit 无效-> 可能会崩溃
  • "使 bar 的 it 无效"。这可能是我困惑的根源。根据en.cppreference.com/w/cpp/container/map/operator_at,“没有迭代器或引用无效。”那么不应该是无效的吗?
  • @AnthonyCalandra:正确,it 不会失效。但是您必须将myMap.find(x); 包裹在锁中,将myMap.end() 包裹在锁中(也许它是安全的,但标准并没有这么说!)您还不如在那时锁定整个功能,而不是零散地锁定它的每个部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-10
  • 1970-01-01
  • 2017-04-15
  • 1970-01-01
  • 1970-01-01
  • 2011-04-14
  • 1970-01-01
相关资源
最近更新 更多