【问题标题】:Is stl vector concurrent read thread-safe?stl 向量并发读取线程安全吗?
【发布时间】:2011-11-19 08:05:16
【问题描述】:

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

我正在寻找以下用例:

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

那么你能告诉我并发读取在向量对象上是否是线程安全的吗?我使用的是 RHEL 6,gcc 版本是 4.5.x

【问题讨论】:

  • 是的,第 3 条没问题。

标签: c++ concurrency stl thread-safety language-lawyer


【解决方案1】:

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


实际上,STL 并不是正确的引用方式。
它是 C++ Standard Library。

C++03 标准根本没有讨论并发,所以并发方面作为编译器的实现细节被忽略了。因此,编译器附带的文档是人们应该寻找与并发相关的答案的地方。

大多数 STL 实现本身不是线程安全的。
但是对于从多个线程并发读取相同对象的大多数 STL 实现确实是线程安全的。

参考资料:

MSDN 说:

单个对象是线程安全的,可以从多个线程中读取。例如,给定一个对象 A,同时从线程 1 和线程 2 读取 A 是安全的。

Dinkumware STL 文档 说:

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

GCC Documentation 说:

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

STL 的 SGI 实现是线程安全的,仅在对不同容器的同时访问是安全的,对共享容器的同时读取访问是安全的意义上说。 如果多个线程访问单个容器,并且至少有一个线程可能写入,则用户负责确保容器访问期间线程之间的互斥。

所以从上面看,是的,在 GCC 中从多个线程同时读取同一个对象是线程安全的。

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

【讨论】:

  • 是的,正确。我已经阅读了 MSDN。但是我在 RHEL 上使用 gcc,我必须 200% 确定并发读取中的线程安全性。
【解决方案2】:

C++0x FDIS (n3290) 中对此有具体提及。

§ 17.6.5.9 避免数据竞争

整个段落都很有趣,但更特别的是:

3/ C++ 标准库函数不得直接或间接修改可由当前线程以外的线程访问的对象 (1.10),除非通过函数的非常量参数直接或间接访问这些对象,包括这个。

表示您可以安全地拨打std::vector<T> 上的cbegin 和cend。以及在std::string 上调用operator== 或operator<。

6/通过调用标准库容器或字符串成员函数获得的迭代器上的操作可以访问底层容器,但不能修改它。

意味着仅仅迭代容器不应该以任何方式修改所述容器。

尽管3/,但似乎还是有全局对象的空间,因为迭代器修改了某种共享寄存器对象,它们会将自己与容器相关联(STL 调试功能)。我不明白:

7/如果对象对用户不可见并且受到保护以防止数据竞争,则实现可以在线程之间共享它们自己的内部对象。

否则。

无论如何,标准保证迭代vector 将是安全的......但在实际读取对象时不做任何保证(那些是你自己的)。在这种情况下,这是因为 std::string 已在上面覆盖。

编辑:正如 David Hammen 公正指出的,该标准尚未完全实施。许多编译器已经提供了上述保证,尽管之前的标准从未提及线程。 MSVC、gcc、clang、icc、comeau 等...所有大牌都应该已经提供了这个保证,从 Als 的回答中可以看出。

【讨论】:

  • 您指的是一个标准,尽管(最终!)批准并发布了,但我所知道的任何编译器供应商尚未完全实施。如果没有人实施该标准,就说某人可以依赖某物,因为标准规定如此,可能还为时过早。
  • @David:嗯,没错。我要澄清的是,这实际上只是设置了所有质量实现已经完成的内容。
【解决方案3】:

除了关于避免数据竞争的通用规则外,该标准在 [container.requirements.dataraces] 中说

-1- 为了避免数据竞争 (17.6.5.9),实现应考虑以下函数为 const:begin、end、rbegin、rend、front、 back、data、find、lower_bound、upper_bound、equal_range、at 以及,除了在关联或无序关联容器中,operator[]。

因此,即使您调用非常量 begin()/end() 等,只要您不实际修改任何内容,它都是安全的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-07
    • 2011-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多