我正在开发在Linux(与G++ 4.3编译)上运行的多线程程序,如果您四处搜索,会发现很多关于std::string在GCC中不是线程安全的可怕故事。据推测,这是由于这样的事实,即它内部使用了“写时复制”功能,对诸如Helgrind这样的工具造成了严重破坏。
我编写了一个小程序,将一个字符串复制到另一个字符串,如果您检查两个字符串,它们都共享相同的内部_M_p指针。修改一个字符串后,指针会发生变化,因此写时复制的东西工作正常。
我担心的是,如果我在两个线程之间共享一个字符串会发生什么(例如,将其作为对象在两个线程之间的线程安全数据队列中传递)。我已经尝试使用'-pthread'选项进行编译,但这似乎并没有太大的区别。所以我的问题是:
我似乎找不到确切的答案,所以希望你们能帮助我。
编辑:
哇,这么短的时间内有很多答案。谢谢!当我要禁用COW时,一定会使用Jack的解决方案。但是现在主要的问题变成:我真的必须禁用COW吗?还是为COW线程完成的“簿记”安全吗?我目前正在浏览libstdc++的源代码,但这需要花费一些时间才能弄清楚...
编辑2
OK浏览了libstdc++源代码,我在libstd++-v3/include/bits/basic_string.h中找到了类似的内容:
_CharT*
_M_refcopy() throw()
{
#ifndef _GLIBCXX_FULLY_DYNAMIC_STRING
if (__builtin_expect(this != &_S_empty_rep(), false))
#endif
__gnu_cxx::__atomic_add_dispatch(&this->_M_refcount, 1);
return _M_refdata();
} // XXX MT
因此,绝对有一些关于引用计数器的原子更改的信息。
结论
我在这里将sellibitze的评论标记为答案,因为我认为我们已经得出结论,该领域目前仍未解决。为了规避COW行为,我建议 jack ·劳埃德(Jack Lloyd)回答。谢谢大家进行有趣的讨论!
最佳答案
线程尚未成为标准的一部分。但是,我认为,如今没有任何供应商可以在不使std::string成为线程安全的情况下摆脱困境。注意:“线程安全”的定义不同,我的可能与您的定义不同。当然,默认情况下保护std::vector之类的容器以进行并发访问几乎没有意义,即使您不需要时也是如此。这会违背C++的“不为不使用的东西付费”的精神。如果用户要在不同线程之间共享对象,则用户应始终负责同步。这里的问题是,即使从用户的角度来看,即使“功能应用于不同的对象”,库组件是否也会使用并共享一些隐藏的数据结构,这些结构也可能导致数据争用。
C++ 0x草案(N2960)包含“避免数据争用”部分,该部分基本上说,当且仅当它主动避免可能的数据争用时,库组件才可以访问对用户隐藏的共享数据。听起来std::basic_string的写时复制实现必须与w.r.t一样安全。多线程是另一种实现,其中内部数据永远不会在不同的字符串实例之间共享。
我不确定libstdc++是否已经解决了这个问题。我认为是的。可以肯定的是,请查看the documentation