STL向量并发读取线程安全吗?

25

我正在开发一个应用程序,预计会有大量的线程遍历一组字符串值,并尝试将自己的数据与列表中可用的数据进行匹配。

我正在寻找以下用例:

  1. 向量被初始化为几个std::string类型的元素。 (假设对象名称为strList)。 strList将在应用程序启动时初始化。
  2. 所有线程都将遍历strList以查看它的值是否与strList的至少一个元素匹配。
  3. 没有线程会尝试修改strList,它将严格用作只读对象。

因此,请告诉我并发读取在向量对象上是线程安全的吗? 我正在使用RHEL 6,gcc版本为4.5.x


2
是的,按照第三点的要求就可以。 - Hans Passant
3个回答

42

针对您提到的场景,它是完全线程安全的。


实际上,STL 不是正确的称呼方式。
应该使用 C++ 标准库

C++03 标准根本没有涉及并发,因此并发方面是留给编译器实现细节的。因此,应该查看随编译器附带的文档以了解与并发相关的答案。

大多数 STL 实现不是线程安全的。但是对于从多个线程中同时读取同一对象的情况,大多数 STL 实现确实是线程安全的。

参考资料:

MSDN 说:

单个对象对于多个线程的读取是线程安全的。例如,对于对象 A,从线程 1 和线程 2 同时读取 A 是安全的。

Dinkumware STL 文档 说:

多个线程可以安全地读取相同的容器对象。(容器对象中没有受保护的可变子对象。)

GCC 文档 说:

我们目前使用SGI STL 的线程安全定义,其中指出:

SGI实现的STL仅在不同容器的并发访问和共享容器的读访问是安全的意义下是线程安全的。如果多个线程访问单个容器,并且至少有一个线程可能会进行写操作,则用户需要确保在容器访问期间线程之间的互斥。

因此,根据上述内容,是的,在GCC中允许多个线程从多个线程同时对同一对象进行并发读取是线程安全的。

注意:GCC的标准库是SGI STL代码的派生版本。


是的,没错。我已经阅读了MSDN。但是我正在RHEL上使用gcc,并且必须对并发读取的线程安全性有200%的把握。 - Amey Jah

10
在C++0x FDIS(n3290)中有特别提到这一点。
§ 17.6.5.9数据竞争避免
整个段落都很有意思,但尤其是:
3/ C ++标准库函数不得直接或间接修改线程以外的对象(1.10),除非这些对象通过函数的非const参数,包括this直接或间接访问。
这意味着您可以安全地调用std :: vector 上的cbegin和cend。以及在std :: string上调用operator ==或operator <。
6/通过调用标准库容器或字符串成员函数获得的迭代器的操作可能会访问底层容器,但不得修改它。
这意味着仅迭代容器不应以任何方式修改所述容器。
尽管有3 /,似乎还有全局对象的空间,因为迭代器修改某种共享寄存器对象,其中它们将自己与容器相关联(STL调试功能)。我不理解7 /:
7/如果对象对用户不可见并受到数据竞争的保护,则实现可以在线程之间共享其自己的内部对象。
否则。
无论如何,标准保证在vector上进行迭代将是安全的......但是在实际阅读对象时不做任何保证(这些是您自己的)。在这种情况下,这是因为std :: string已在上面提到。
编辑:正如David Hammen所指出的那样,此标准尚未完全实现。许多编译器已经提供了上述保证,即使以前的标准从未涉及线程。 MSVC,gcc,clang,icc,comeau等...所有大牌都应该已经提供此保证,正如Als的答案所示。

您所提到的标准,虽然(终于!)已经获得批准并发布,但据我所知,任何编译器供应商都尚未完全实现。仅仅因为标准规定如此而说某人可以依赖某事是不太妥当的,特别是在没有人实现该标准的情况下。 - David Hammen
@David:嗯,没错。我会澄清一下,实际上这只是设置了所有优质实现已经完成的内容。 - Matthieu M.

2
除了关于数据竞争避免的通用规则外,在 [container.requirements.dataraces] 中,标准规定如下:
-1- 为了避免数据竞争(17.6.5.9),实现应将以下函数视为 constbeginendrbeginrendfrontbackdatafindlower_boundupper_boundequal_rangeat 以及在非关联或无序关联容器中,operator[]
因此,即使您调用非常量的 begin()/end() 等函数,只要您不实际修改任何内容,就是安全的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接