STL std::string 类将导致多处理器计算机的崩溃和内存损坏

重要说明:本文是由 Microsoft 机器翻译软件进行的翻译并可能由 Microsoft 社区通过社区翻译机构(CTF)技术进行后期编辑,或可能是由人工进行的翻译。Microsoft 同时向您提供机器翻译、人工翻译及社区后期编辑的文章,以便对我们知识库中的所有文章以多种语言提供访问。翻译的文章可能存在词汇、句法和/或语法方面的错误。Microsoft 对由于内容的误译或客户对内容的使用所导致的任何不准确、错误或损失不承担责任。

点击这里察看该文章的英文版: 813810
不再更新的 KB 内容免责声明
本文介绍那些 Microsoft 不再提供支持的产品。因此本文按“原样”提供,并且不再更新。
症状
在 Microsoft Visual C++ 6.0 中使用应用程序提供的标准模板库 (STL) 生成时,可能会出现内存损坏,或您的计算机可能会停止响应。这些症状在多处理器计算机上发生得更为频繁。以前,也许在单处理器计算机未出现此类问题适用相同的代码。在调试器中出错的线程检查时,通常会看到内存管理函数中的失败。经常看到basic_string<char...></char...>类中的堆栈跟踪的方法。由于内存损坏也是一种症状,故障可能会显示不相关的字符串处理的区域。

下面是崩溃的此问题所在导致堆栈跟踪的示例 ︰
01 0012ebc4 77fb4014 0246ffd0 00000027 02531000 ntdll!RtlpDphReportCorruptedBlock+0x8c02 0012ebec 77fb2cb1 02531000 01001002 0246ffd0 ntdll!RtlpDphNormalHeapFree+0x4603 0012ec10 77fb5653 02530000 01001002 0246ffd0 ntdll!RtlpDebugPageHeapFree+0xa604 0012ec88 77fa760a 02530000 01001002 0246ffd0 ntdll!RtlDebugFreeHeap+0x20305 0012ed28 77fcba9e 02530000 01001002 0246ffd0 ntdll!RtlFreeHeapSlowly+0x4d06 0012edcc 004065a6 02530000 00000000 0246ffd0 ntdll!RtlFreeHeap+0x5307 0012ee14 0041353a 0246ffd0 00404198 0246ffd0 main!free+0xda08 0012ee1c 00404198 0246ffd0 0012eecc 004e9b70 main!operator delete+0x9 (FPO: [1,0,0]) (CONV: cdecl) [afxmem.cpp @ 349]09 0012ee38 00402a71 02477fe0 00000011 004e9ce0 main!basic_string<char,char_traits_char,allocator<char> >::append_helper+0x68 (FPO: [EBP 0x0012eecc] [2,1,4]) (CONV: thiscall)...
NTDLL! 77f97710()NTDLL! 77fb5721()NTDLL! 77fa760a()NTDLL! 77fcba9e()MSVCRT! 78001d92()operator delete(void * 0x00c266f8) line 6 + 10 bytesstd::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Tidy(std::basic_string<char,std::char_traits<char>,std::allocator<char> > * const 0x0000000f {???}, unsigned char 1) line 591 + 6 bytes...
00 0184fb9c 60f3abc3 main!__sbh_free_block+0x17301 0184fbb4 60f2aa93 main!free+0x2802 0184fbbc 60f2423c main!operator delete+0x903 0184fce8 60f244b0 main!function(std::basic_string<char,std::char_traits<char>,std::allocator<char> > var = std::basic_string<char,std::char_traits<char>,std::allocator<char> >)+0x79c...
...5ed 0198de20 77fac5f4 0198dec0 0198e3f8 0198dedc ntdll!ExecuteHandler+0x265ee 0198dea8 77f91a96 0198dec0 0198dedc 0198dec0 ntdll!RtlDispatchException+0x765ef 0198df14 77b22546 2cb01468 47ac0008 00000008 ntdll!KiUserExceptionDispatcher+0xe5f0 0198e340 1001b22c 00ed0000 00000000 00000080 ole32!SyncStubInvoke+0x615f1 0198e37c 1001b123 00000080 1001a4ef 00000080 main!_heap_alloc+0xed5f2 0198e384 1001a4ef 00000080 00000001 100022f1 main!_nh_malloc+0x10 (FPO: [2,0,0])5f3 0198e390 100022f1 00000080 0000007c 0198f430 main!operator new+0xb (FPO: [1,0,0])5f4 0198e3b0 10002207 0000003c 0000007d 0198f42c main!std::basic_stringbuf<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::overflow+0x83 (CONV: thiscall) [C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE\sstream @ 60]5f5 0198e3cc 10003194 00000000 0000006b 0198f6e0 main!std::basic_streambuf<unsigned short,std::char_traits<unsigned short> >::xsputn+0x6a (CONV: thiscall) [C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE\streambuf @ 166]5f6 0198e404 10005621 0198f42c 010113b2 1003573c main!std::operator<<+0xb0 (CONV: cdecl) [C:\Program Files\Microsoft Visual Studio\VC98\INCLUDE\ostream @ 305]...
原因
Microsoft Visual C++ 6.0 中包含的标准模板库 (STL) 是不安全的多线程应用程序。特别是, std::string类的实现取决于basic_string<...></...>模板类。Basic_string<...></...>模板类引用计数的隐藏的字符缓冲区的副本。Basic_string<...></...>模板类存储在 8 位无符号字符计数。此实施后会出现以下的一般问题 ︰
  • Basic_string<...></...>模板类实现不 protectthe 计数与所需的线程同时运行的多处理器计算机上的同步机制。多线程正在运行 onsingle 处理器的计算机的 STL 代码避免此问题,因为只有一个线程运行时执行非常,并且内存读取或对整数写入操作完成之前 threadcan 的另一个中断。
  • 写入到std::string在一个线程中的类可能破坏 readingof std::string类,例如分配,另一个线程中创建的副本。Thesupposed 副本共享同一个隐藏的字符缓冲区。
  • 字符串损坏可能发生位置的指针或 referenceto std::string类是线程间共享。通常,它是程序员能够避免这种情况的责任。
解决方案
您必须重新生成应用程序后使 STL 线程安全。获取线程安全 STL 的首选的方法是升级到较新的版本,在当前的 Visual C++ 标准基于 STL。但是,基于当前的 Visual C++ 标准 STL 不与 STL 在 Microsoft Visual C++ 6.0 作为一种新产品发布时可用的相同。但是,升级到较新版本可能微不足道,这取决于您的应用程序使用的 STL 函数。若要获得对 STL 的新版本,请使用以下方法之一 ︰

方法 1 ︰ 使用 Microsoft Visual C++.NET (7.0 及更高版本)

允许在应用程序中,打开 Visual C++ 项目 automaticallyconvert 为新项目的格式,向该项目,然后重新生成。是线程安全的std::string classimplementation 在此版本中的所述的问题。如果 youuse DLL 运行库功能您任何一种应用程序中的项目,则必须重新生成应用程序分发新的 Visual C++ 运行时组件 (如 Msvci7x.dll、 Msvcp7x.dll 和 Msvcr7x.dll)。

注意:不需要分发到客户端计算机 Microsoft.NET Framework 使用 Microsoft Visual C++ 的.NET。

方法 2 ︰ 使用 Microsoft Visual C++ 6.0 替换 STL 从第三方

依产品数据集成的详细信息和 individualvendors 提供的支持。从一个来源提供后续 STL 版本是 Dinkumware 有限公司,其中 Microsoft 许可证 Visual C++ 6.0 STL 的公司。该 isclaimed,它可以与现有的集成构建流程。有关详细信息,以及已知的错误和解决方法的列表,请访问下面的 Dinkumware 网站 ︰ Microsoft 提供的第三方联系人信息,以帮助您查找技术支持。此联系信息如有更改,恕不另行通知。Microsoft 不能保证第三方联系信息的准确性。 本文讨论的第三方产品是由与 Microsoft 无关的公司生产的。对这些产品的性能或可靠性,微软并不保证,暗示或其他方式。
替代方法

解决 Microsoft Visual C++ 6.0 STL 中的 std::string 类问题

如果不升级到 STL 的新版本,您可以尝试纠正中标准的 Microsoft Visual C++ 6.0 安装的std::string类线程安全问题。尽管有几个 Microsoft Visual C++ 6.0 STL 中的类的多线程处理问题,到目前为止最常见的和有问题的类是std::string类。下面的步骤和解决方法是应急措施,以确保应用程序工作正常,并且措施留出时间来研究其它替代方案。请考虑下列步骤可创建新的代码路径和可能是在整个应用程序的行为。全面测试按照在广泛部署前一家公司或个人的软件策略重新生成应用程序。

禁用的字符串引用计数

每个记录在该部分的解决方法要求您先禁用引用计数机制。若要禁用引用计数,则必须修改<xstring></xstring>头文件和设置为0_FROZEN枚举常量。在默认安装中,<xstring>头文件位于以下位置 ︰</xstring>
C:\Program 该 Visual Studio\VC98\Include
_FROZEN枚举常数更改为0<xstring></xstring>头文件在第 62 行,以便它看起来类似于以下 ︰
enum _Mref {_FROZEN = 0}; // set to zero to disable sharing; original value 255
如果您按照此建议,并重新生成使用这些头文件的所有软件, std::string类代码将更多的线程安全。没有为该语句的一些注意事项。因此,请仔细阅读下面的替代方法指南。禁用引用计数通过将_FROZEN枚举常量设置为0<xstring></xstring>头文件,请使用以下方法来变通解决此问题之一。

方法 1 ︰ 使用静态 CRT 链接

修改所有 projectsthat 使用std::string类来链接到静态版本的 Microsoft 运行-timelibrary (CRT) 中的项目设置。如果您的项目还启用了共享 DLL 中的 UseMFC设置,不能使用此方法。每个项目,请执行以下步骤 ︰
  1. 打开的项目。
  2. 项目菜单上,单击设置
  3. 配置列表中,单击发布
  4. 单击C/c + +选项卡,然后单击分类列表中的代码生成
  5. 运行时库列表中,单击多线程 (/ MT)
  6. 配置列表中,单击调试
  7. 运行时库列表中,单击多线程调试 (/ MTd)
  8. 如果配置列表中的其他配置,设置适当的运行时库还为这些选项。
  9. 单击确定,然后重新生成项目。
此替代方法可确保您的所有代码使用都修改的版本<xstring></xstring>到 thewhole 多线程运行时库,其中包括 MFC 静态链接的文件。可能存在的一个问题是最终的代码大小将大于动态链接的版本,可能是哪部这样。

方法 2 ︰ 使用 CRT 的动态链接

如果项目代码必须链接到 DLL 作为运行时 library(CRT),则必须采取另一种方法。CRT 的动态链接是 DLL 项目的默认设置。依赖其他组件被授权使用这种 asMFC 或第三方库中使用您的应用程序,typicallyrequire 动态链接到 CRT。如果唯一的依赖是在静态库中使用 MFC的选项,MFC,您的 canuse,并应用方法 1。默认情况下,当您在 Microsoft Visual C++ 6.0 中,创建配置项目将使用 aDLL 从 CRT。

动态的 CRT 链接项目设置链接一些std::string类中的方法名为 Msvcp60.dll 的预构建 Microsoft CRTDLL 您装载的实现。因为 Microsoft 构建该 DLL 使用未修改的<xstring></xstring>头文件、 改为对本地 copyof _FROZEN常量<xstring></xstring>不承兑出 thatlibrary 调用函数。这些包括在 Msvcp60.dll 文件中提供的功能如_Tidy()assign()<char></char><short></short> basic_string类的实例化。Basic_string类是std::string类的基础。

若要使用您的 Microsoft 提供的 Msvcp60.dll 文件中实现的 modulesinstead std::string类的静态实现,请执行以下步骤 ︰
  1. 在<xstring>文件中,注释掉下面的代码,它位于文件的末尾。若要执行此操作,可将代码放在#if 0 / #endif块中 ︰</xstring>
    #ifdef _DLL#pragma warning(disable:4231) /* the extern before template is a non-standard extension */extern template class _CRTIMP basic_string<char, char_traits<char>, allocator<char> >;extern template class _CRTIMP basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >;#pragma warning(default:4231) /* restore previous warning */#endif  // _DLL
  2. 其他字符串运算符定义在<string>头文件中,并且它们还包括 CRT 中通过 Msvcp60.dll 文件。您注意到有一系列的"..._CRTIMP 外部模板"的<string>文件的结尾处受保护的#ifdef _DLL子句就像在<xstring>文件中的定义。注释掉所有这些定义还 ︰</xstring> </string> </string>
    #ifdef _DLL #pragma warning(disable:4231) /* the extern before template is a non-standard extension */ extern template class _CRTIMP    basic_string<char, char_traits<char>, allocator<char> > __cdecl operator+(        const basic_string<char, char_traits<char>, allocator<char> >&,        const basic_string<char, char_traits<char>, allocator<char> >&);extern template class _CRTIMP    basic_string<char, char_traits<char>, allocator<char> > __cdecl operator+(...extern template class _CRTIMP    basic_ostream<wchar_t, char_traits<wchar_t> >& __cdecl operator<<(        basic_ostream<wchar_t, char_traits<wchar_t> >&,        const basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >&);#pragma warning(default:4231) /* restore previous warning */#endif      // _DLL
  3. 保存对这些文件的修改,然后重新生成应用程序,使用 STL 的所有项目。如果您的项目中声明类 __declspec(dllexport)类,该类包含std::string类型的成员,您将看到 C4251 警告。因为所有代码的都生成与std::string类现在静态链接,则可以忽略这些警告。若要显式禁用这些警告,请使用以下表示法 ︰
    #pragma warning(disable: 4251)
这种方法可以平衡比std::string类 Dll 中的 MFC 和 CRT 文本的使用。还有少量增加使用std::string类每个正好模块中的代码大小。

方法 3 ︰ 使用聪明的黑客以避免链接问题

创建 typedef无符号的字符,并使用,而不是现有的std::string类 typedef。Typedef 可能需要包括在头文件中使用std::string类的 sourcefiles 应用程序中的窗体。Typedef 可能类似于以下内容 ︰
typedef std::basic_string<unsigned char> MyString;#define string MyString
Usedwith 必须强制转换或作为无符号字符型处理此类任何字符串文本。有可能会增加代码大小和易于实现,并且有较少 linkageside 效果。

方法 4 ︰ 使用自定义 std::string DLL

此选项可以使您受益将std::string类实现放在一个 DLL 中的最小代码 sizeby 的好处。创建导出的std::string类 DLLproject。将链接到该 DLL,而不是 thestandard Msvcp60.dll 文件。您必须重新发布此新的 DLL 放在一起的 withyour 应用程序。这是一个高级的选项。
更多信息
下面的 c + + 代码示例演示一个可能会出现缺乏同步时的情形 ︰
...std::string	A;A = "Init";_beginthread(Thread1, 0, (void*)&A);_beginthread(Thread2, 0, (void*)&A);A = "";...void Thread1(void* arg){	std::string	A1 = *(std::string*)arg;	...	A1 = "newval";}void Thread2(void* arg){	std::string	A2 = *(std::string*)arg;	...	std::string	B = A2;	A2 = "newval2";}
在此示例中,Thread1 将显示输入参数的副本,并引发上为 1 共享的字符缓冲区的引用计数。它工作,Thread2 还发出一份其输入参数而引发的引用数为 2。同时,主线程将新值分配给一个、 创建新字符缓冲区,并降 1 到的原始共享缓冲区上的引用计数。

Thread1 开始创建新分配为 A1 的新字符缓冲区,积极引用计数具结其以前共享的字符缓冲区中,然后计算的 1 到 0 的递减。同时,Thread2 也是分配到 B B 的过程中共享的字符缓冲区的 A2,并引发上 A2 中的字符缓冲区的引用计数尝试以将其递增到 2 只是之前 Thread1 写于引用计数为 0。引用计数现 0 而不是 1。引用计数就已 0 如果对的引用计数器的访问已进行同步。

当 Thread2 将新值分配给 A2 时,Thread2 看到引用计数为 0,并放弃 B 仍引用原来的共享的字符缓冲区。目前可供其他应用程序中使用保留字符缓冲区的内存。但是,std::string B 仍然持有一个指针到字符的缓冲区。损坏和崩溃会导致以下情况 ︰
  • B 尝试释放的字符缓冲区。
  • B 会尝试读取其他应用程序代码覆盖与实时数据字符缓冲区的内容。
  • 有尝试来扩展或修改字符串。
参考
有关 Visual C++ 语言和编译器问题的详细信息,请参阅是附带 VC + + 对 STL?在下面的 Microsoft 最有价值专家 (MVP) 的网站的主题 ︰
崩溃

属性

文章 ID:813810 - 上次审阅时间:05/07/2016 23:41:00 - 修订版本: 1.0

  • kbthreadsync kbprb kbmt KB813810 KbMtzh
反馈