4

我正在迭代一个 STL 向量并从中读取值。还有另一个线程可以更改此向量。现在,如果其他线程从向量中插入或删除元素,它会使迭代器无效。没有使用涉及的锁。我选择通过索引(方法 1)代替迭代器(方法 2)访问容器是否使其线程安全?性能呢?

struct A{int i; int j;};

方法一:

   size_t s = v.size();//v contains pointers to objects of type A
    for(size_t i = 0; i < s; ++i)
    {
         A* ptr = v[i];
         ptr->i++;
    }

方法二:

std::vector<A*>::iterator begin =  v.begin();
std::vector<A*>::iterator end =  v.end();
for(std::vector<A*>::iterator it = begin; it != end; ++it)
{
     A* ptr = *it;
     ptr->i++: 
}
4

4 回答 4

4

标准库容器的线程安全保证非常简单(这些规则是在 C++ 2011 中添加的,但基本上所有当前的库实现都符合这些要求并施加相应的限制):

  1. 可以有多个并发读者
  2. 如果有一个线程修改容器,则不应有其他线程访问(读取或写入)它
  3. 要求是每个容器对象

实际上,这意味着您需要使用容器外部的某种机制来保证从多个线程访问的容器得到正确处理。例如,您可以使用互斥锁或读写器锁。当然,大多数时候容器只能从一个线程访问,并且没有任何锁定就可以正常工作。

如果不使用显式锁,您将导致数据竞争并且行为未定义,与您使用索引还是迭代器无关。

于 2012-05-13T03:32:02.353 回答
3

不,STL 容器不是线程安全的。

您应该在每个线程访问向量时提供对每个线程(删除的线程/添加的线程)的独占访问权限。即使在使用索引时,您也可能会删除第 i 个元素,从而使您检索到的指针无效。

于 2012-05-13T02:55:37.943 回答
3

OP “我选择通过索引(方法 1)而不是迭代器(方法 2)访问容器是否使其线程安全?”

不,一旦您开始写入数据结构,这两种方法都不是线程安全的。

因此,您需要序列化对数据结构的访问。

为了节省您大量的时间和挫败感,有很多现成的解决方案,例如

英特尔线程构建模块 (TBB),带有线程安全容器,例如concurrent_vector.

http://threadingbuildingblocks.org/

concurrent_vector 是一个具有以下特性的容器:

  • 按索引随机访问。第一个元素的索引为零。
  • 多个线程可以同时增长容器并添加新元素。
  • 增长容器不会使现有的迭代器或索引无效。*

OP“性能怎么样?”

不得而知。在具有不同编译器的不同系统上的不同性能,但不知道其大到足以影响您的选择。

于 2012-05-26T12:50:37.253 回答
0

您的算法可以使用固定大小的数组吗?

我问的原因是,从逻辑上讲,让多个线程以线程安全、无锁的方式修改(大多数类型的)容器的唯一方法是使容器本身保持不变。这意味着 CONTAINER 在线程中永远不会改变,只会改变其中的元素。想一想在火车上弄乱棚车内部与在火车沿轨道移动时实际从火车上添加和移除整个棚车之间的区别。仅当您对该数据的操作遵守某些约束时,即使干预元素也是安全的。

好消息是锁并不总是世界末日。如果多个执行上下文(线程、程序等)可以同时命中同一个对象,那么它们通常是唯一的解决方案。

于 2014-08-27T20:37:55.480 回答