C ++ STL向量迭代器与索引访问和线程安全性

时间:2012-05-13 02:52:25

标签: c++ multithreading stl thread-safety

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

struct A{int i; int j;};

方法1:

   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++;
    }

方法2:

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 个答案:

答案 0 :(得分:4)

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

  1. 可以有多个并发读者
  2. 如果有一个线程修改容器,则不应有其他线程访问(读或写)
  3. 要求是每个容器对象
  4. 实际上,这意味着您需要使用容器外部的某种机制来保证从多个线程访问的容器得到正确处理。例如,您可以使用互斥锁或读写器锁。当然,大多数时候只能从一个线程访问容器,而且没有任何锁定就可以正常工作。

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

答案 1 :(得分:3)

没有。 STL容器不是线程安全的。

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

答案 2 :(得分:3)

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

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

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

为了节省大量时间和挫折,有许多现成的解决方案,例如。

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

http://threadingbuildingblocks.org/

  

concurrent_vector是具有以下功能的容器:

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

OP“性能怎么样?”

不可知。不同编译器在不同系统上的不同性能,但不知道其大小足以影响您的选择。

答案 3 :(得分:0)

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

我要问的是,从逻辑上讲,以线程安全,无锁方式修改(大多数种类)容器的多个线程的唯一方法是使容器本身不变。这意味着CONTAINER不会在线程内发生变化,只会在其中的元素中发生变化。想想搞乱火车上的厢式车内部与实际添加&amp;从火车上移走轨道时,从整列火车上移走整个箱车。如果您对该数据的操作遵守某些约束条件,即使干涉元素也是安全的。

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