首页 文章

stl vector并发读取线程安全吗?

提问于
浏览
21

我正在开发一个应用程序,其中大量的线程需要迭代一组字符串值,并尝试将其自己的数据与列表中的可用数据进行匹配 .

我正在寻找以下用例:

  • Vector初始化为几个std :: string类型的元素 . (可以说对象名是strList) . strList将在应用程序启动时初始化 .

  • 所有线程将遍历strList以查看它的值是否与strList的至少一个元素匹配 .

  • 没有线程会尝试修改strList,它将严格用作只读对象 .

那么请告诉我并发读取是否对矢量对象是线程安全的 . 我使用的是RHEL 6,gcc版本是4.5.x

3 回答

  • 37

    YES 对于你提到的场景,它是完全线程安全的 .


    实际上,STL并不是一种正确的引用方式 .
    这是 C++ Standard Library .

    C 03标准根本不讨论并发性,因此并发性方面被省略为编译器的实现细节 . 因此,编译器附带的文档是应该查找与并发相关的答案的地方 .

    大多数STL实现都是 not 线程安全的 .
    但是对于来自多个线程的同一对象的并发读取,大多数STL实现确实是线程安全的 .

    References:

    MSDN 说:

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

    The Dinkumware STL-Documentation 说:

    多个线程可以安全地读取同一个容器对象 . (容器对象中有nunprotected mutable子对象 . )

    GCC Documentation 说:

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

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

    所以从上面可以看出,GCC中的线程安全是从多个线程并发读取同一个对象 .

    注意:GCC的标准库是SGI的STL代码的衍生物 .

  • 9

    为此,在C 0x FDIS(n3290)中有一个特别的提及 .

    §17.6.5.9避免数据竞争

    整段是有意义的,但更具体的是:

    3 / C标准库函数不应直接或间接修改除当前线程以外的线程可访问的对象(1.10),除非通过函数的非const参数直接或间接访问对象,包括此参数 .

    意味着您可以安全地在 std::vector<T> 上调用 cbegincend . 以及在 std::string 上调用 operator==operator< .

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

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

    虽然3 /尽管如此,但似乎有全局对象的空间,因为迭代器修改某种共享的寄存器对象,在这些对象中它们将自己与容器相关联(STL调试功能) . 我没理解:

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

    除此以外 .

    无论如何,标准保证迭代 vector 将是安全的...但是在实际读取对象时(这些是你自己的)不能保证 . 在这种情况下,这包括在内,因为上面介绍了 std::string .

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

  • 2

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

    -1-为避免数据争用(17.6.5.9),实现应考虑以下函数为const:begin,end,rbegin,rend,front,back,data,find,lower_bound,upper_bound,equal_range,at和除关联或无序关联容器外,运算符[] .

    所以,即使你只是在不安全的情况下调用非const begin() / end() 等等 .

相关问题