繁体   English   中英

多线程程序中的std :: string

[英]std::string in a multi-threaded program

鉴于:

1)C ++ 03标准没有以任何方式解决线程的存在

2)C ++ 03标准由实现决定是否std::string在其复制构造函数中使用写时复制语义

3)写时复制语义通常导致多线程程序中的行为无法预测

我得出以下看似有争议的结论:

您根本无法安全,可移植地在多线程程序中使用std :: string

显然,没有STL数据结构是线程安全的。 但是至少,例如,对于std :: vector,您可以简单地使用互斥锁来保护对向量的访问。 使用使用COW的std :: string实现,如果不编辑供应商实现内部的引用计数语义,您甚至无法可靠地做到这一点。

实际示例:

在我的公司中,我们有一个多线程应用程序,该应用程序已经过全面的单元测试,并且在Valgrind中运行了无数次。 该应用程序运行了几个月,没有任何问题。 有一天,我在另一版本的gcc上重新编译了该应用程序,突然之间我一直都出现随机段错误。 Valgrind现在报告了std :: string复制构造函数中libstdc ++内部的无效内存访问。

那么解决方案是什么? 好吧,当然,我可以将typestd std::vector<char>为字符串类-但是,确实很糟糕。 我也可以等待C ++ 0x,我祈祷这将需要实现者放弃COW。 或者,(颤抖),我可以使用自定义字符串类。 我个人总是反对在已有的库可以正常工作时实现自己的类的开发人员,但是老实说,我需要一个字符串类,我可以确定它不使用COW语义。 和std :: string根本不能保证这一点。

我说得对不对那std::string根本无法可靠地在所有便携式,多线程程序中使用? 有什么好的解决方法?

您不能在多线程程序中安全且可移植地执行任何操作。 根本没有可移植的多线程C ++程序之类的东西,正是因为线程将C ++所说的所有内容(包括操作顺序以及修改任何变量的结果)抛到了窗外。

标准中也没有任何内容可以保证vector可以按照您所说的方式使用。 提供具有线程扩展的C ++实现是合法的,也就是说,在其中进行初始化的线程外对向量的任何使用都会导致未定义的行为。 启动第二个线程的那一刻,您不再使用标准的C ++,并且必须向编译器供应商寻求什么是安全的,什么不是安全的。

如果您的供应商提供了线程扩展,并且还提供了带有COW的std :: string(因此)无法使其成为线程安全的,那么我认为暂时您的观点是与您的供应商或线程扩展有关,而不是符合C ++标准。 例如,可以说POSIX在使用pthreads的程序中应该禁止COW字符串。

您可以通过使用单个互斥锁来确保其安全,该互斥锁可以在进行任何字符串突变以及复制后读取的任何字符串时使用。 但是您可能会对该互斥体产生残酷的争论。

你是对的。 这将在C ++ 0x中修复。 现在,您必须依靠实现的文档。 例如,最新的libstdc ++版本(GCC)使您可以使用字符串对象,就像没有字符串对象与另一个缓冲区共享其缓冲区一样。 C ++ 0x强制实现库以保护用户免受“隐藏共享”的影响。

鉴于该标准没有提及内存模型,并且完全不知道线程,我想说您不能肯定地假设每个实现都不是牛,所以不能,

除此之外,如果您了解自己的工具,则大多数实现将使用非牛字符串来允许多线程。

观察它的更正确方法是“您不能在多线程环境中安全且可移植地使用C ++”。 也不能保证其他数据结构也可以合理地表现。 否则运行时不会使您的计算机崩溃。 该标准不保证有关线程的任何内容。

因此,要对C ++中的线程执行任何操作 ,都必须依赖于实现定义的保证。 然后,您可以安全地使用std::string因为每个实现都会告诉您在线程环境中使用是否安全。

产生第二个线程的那一刻,您就失去了真正可移植性的所有希望。 std::string不比其他语言/库“轻便”。

您可以使用STLport。 它提供非COW字符串。 它在不同平台上具有相同的行为。

本文介绍了基于STLport字符串,绳索和GNU libstdc ++实现的具有写时复制和非写时复制算法的STL字符串的比较。

在我工作的公司中,我有一些运行在HP-UX 11.31上使用STLport构建而没有STLport的服务器应用程序的经验。 该应用程序使用优化级别为O2的gcc 4.3.1进行了编译。 因此,当我运行使用STLport构建的程序时,与不使用STLport构建的相同程序(使用gcc自己的STL库)相比,它处理请求的速度提高了25%。

我分析了这两个版本,发现与具有STLport的版本(1%)相比,没有STLport的版本在pthread_mutex_unlock() (2.5%)中花费的时间要多得多。 不带STLport的版本中的pthread_mutex_unlock()本身是从std :: string函数之一调用的。

但是,在进行性能分析后,我以这种方式将最常称为函数的字符串分配更改为:

string_var = string_var.c_str(); // added .c_str()

没有STLport的版本的性能有了显着提高。

我管理字符串访问:

  • std::string成员std::string私有
  • 为getter返回const std::string&
  • 设置员修改成员

这对我来说一直很好,并且是正确的数据隐藏。

如果要禁用COW语义,则可以强制字符串进行复制:

// instead of:
string newString = oldString;

// do this:
string newString = oldString.c_str();

如前所述,特别是如果您可以嵌入null,则应使用迭代器ctor:

string newString(oldString.begin(), oldString.end());

在MSVC中,std :: string不再是引用计数的指向容器的共享指针。 他们在每个副本构造函数和赋值运算符中按值选择全部内容,以避免多线程问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM