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

C++ C++;STL向量迭代器vs索引访问和线程安全,c++,multithreading,stl,thread-safety,C++,Multithreading,Stl,Thread Safety,我在一个STL向量上迭代并从中读取值。还有另一个线程可以对此向量进行更改。现在,如果另一个线程从向量中插入或删除and元素,它将使迭代器无效。这里面没有锁的用处。我选择通过索引(方法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;

我在一个STL向量上迭代并从中读取值。还有另一个线程可以对此向量进行更改。现在,如果另一个线程从向量中插入或删除and元素,它将使迭代器无效。这里面没有锁的用处。我选择通过索引(方法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++;
    }
size\u t s=v.size()//v包含指向A类型对象的指针
对于(尺寸i=0;ii++;
}
方法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++: 
}
std::vector::迭代器begin=v.begin();
std::vector::迭代器end=v.end();
for(std::vector::iterator it=begin;it!=end;++it)
{
A*ptr=*it;
ptr->i++:
}

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


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

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

  • 可以有多个并发读卡器
  • 如果有一个线程修改一个容器,那么就不能有其他线程访问(读或写)它
  • 这些要求是针对每个容器对象的
  • 实际上,这意味着您需要使用容器外部的某种机制来确保正确处理从多个线程访问的容器。例如,您可以使用互斥锁或readerwriter锁。当然,大多数情况下,容器只从一个线程访问,无需任何锁定即可正常工作

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

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

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

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

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

    英特尔线程构建块(TBB),它带有线程安全容器,如
    concurrent\u vector

    并发_向量是具有以下特征的容器:

    • 按索引随机访问。第一个元素的索引为零
    • 多个线程可以同时增加容器和附加新元素
    • 增加容器不会使现有迭代器或索引无效*
    OP“性能如何?”


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

    您的算法是否可以使用固定大小的数组

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


    好消息是锁并不总是世界末日可以同时击中同一个对象,它们通常是唯一的解决方案。C//P> + 1在C/C++标准库中没有任何东西被认为是线程安全的。POSIX定义的函数被定义为线程安全的。@ SELJUQ70:您的说法是错误的。在发布最新的STNADADARS更新之前,这是正确的,但是C和C++都是正确的。提供特定的线程安全保证。它们可能不是您想要的,但这并不意味着库构造不是线程安全的。各种类实际上没有监视器(在大多数情况下)这似乎是许多人误解为线程安全的地方。@DietmarKühl看了我的笔记后,你似乎完全正确。几乎所有系统和库函数都被认为是线程安全的。此外,我错误地认为所有POSIX函数都是线程安全的。例如,readdir和strerror没有被定义为线程安全的(虽然我认为它们可以)。哪一个更快?其中之一。它会随着平台、编译器、编译器版本、编译器标志以及可能的月球阶段而变化。您需要进行分析。值得注意的是,与被访问对象的位置相比,使用索引和迭代器的选择可能是微不足道的。