文章編號: 813810 - 上次校閱: 2004年6月1日 - 版次: 1.0

在多重處理器電腦上會導致損毀和記憶體損毀 STL std:: string 類別

系統提示本文適用於您使用的作業系統之外的作業系統。與您不相關的文章內容已停用。

在此頁中

全部展開 | 全部摺疊

徵狀

當您建置在 Microsoft Visual C++ 6.0 中, 使用所提供的標準樣板程式庫 (STL), 應用程式可能會發生記憶體損毀, 或您的電腦可能會停止回應。 在多重處理器電腦上更頻繁地發生這些徵狀。 先前, 相同的程式碼沒有這類問題可能曾在單一 - 處理器電腦上。 當您檢查, 偵錯工具中執行緒錯誤通常會看到在記憶體管理函數失敗。 您經常看到堆疊追蹤中的 basic_string < char. ... > 類別方法。 因為記憶體損毀也是徵狀, 失敗可能出現在區域, 是與字串處理無關。

這個問題, 是造成損毀的原因如下的堆疊追蹤範例:
01 0012ebc4 77fb4014 0246ffd0 00000027 02531000 ntdll!RtlpDphReportCorruptedBlock+0x8c
02 0012ebec 77fb2cb1 02531000 01001002 0246ffd0 ntdll!RtlpDphNormalHeapFree+0x46
03 0012ec10 77fb5653 02530000 01001002 0246ffd0 ntdll!RtlpDebugPageHeapFree+0xa6
04 0012ec88 77fa760a 02530000 01001002 0246ffd0 ntdll!RtlDebugFreeHeap+0x203
05 0012ed28 77fcba9e 02530000 01001002 0246ffd0 ntdll!RtlFreeHeapSlowly+0x4d
06 0012edcc 004065a6 02530000 00000000 0246ffd0 ntdll!RtlFreeHeap+0x53
07 0012ee14 0041353a 0246ffd0 00404198 0246ffd0 main!free+0xda
08 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 bytes
std::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+0x173
01 0184fbb4 60f2aa93 main!free+0x28
02 0184fbbc 60f2423c main!operator delete+0x9
03 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+0x26
5ee 0198dea8 77f91a96 0198dec0 0198dedc 0198dec0 ntdll!RtlDispatchException+0x76
5ef 0198df14 77b22546 2cb01468 47ac0008 00000008 ntdll!KiUserExceptionDispatcher+0xe
5f0 0198e340 1001b22c 00ed0000 00000000 00000080 ole32!SyncStubInvoke+0x61
5f1 0198e37c 1001b123 00000080 1001a4ef 00000080 main!_heap_alloc+0xed
5f2 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]
...

發生的原因

標準樣板程式庫 (STL) 在隨附於 Microsoft Visual C++ 6.0 對多執行緒應用程式而言不安全的。 請特別注意, std:: string 類別實作取決於 basic_string < ... > 樣板類別。 basic_string < ... > 範本類別參考會計算隱藏字元緩衝區的複本。 basic_string < ... > 範本類別儲存在 8 位元不帶正負號 char 的計數。 這個實作之後會發生下列一般性問題:
  • basic_string < ... > 樣板類別並不會保護計數與所需之執行緒在同時執行多處理器電腦上同步處理機制。 因為只能有一個執行緒, 時間和記憶體讀取在執行或另一個執行緒可以中斷之前已完成寫入在整數 STL - 單一處理器電腦上執行的多執行緒程式碼中避免這個問題。
  • 可能會寫入至 std:: string 的損在一個執行緒類別毀讀取的 std:: string 類別, 例如其中一個工作分派, 在另一個執行緒所建立的複本。 應該複製共用同一個隱藏字元緩衝區。
  • 指標或參考加入至類別 std:: string 是執行緒之間共用的位置可能發生損毀字串。 通常, 它負責減輕程式設計人員若要避免這種情形。

解決方案

您必須在進行安全執行緒 (Thread - Safe STL 重建應用程式。 慣用的方法, 以取得安全執行緒 (Thread - Safe STL 是, STL 升級至較新版本是根據目前的 Visual C++ 標準。 不過, 無法以目前的 Visual C++ 標準為基礎 STL 相當於 STL 在時間, 以新產品已經發行 Microsoft Visual C++ 6.0 所用。 不過, 升級至新版本可能微不足道取決於 STL 函式, 您的應用程式使用。 若要取得新版本的安全執行緒 (Thread - Safe STL, 使用的下列方法之一:

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

允許專案, 會自動轉換成新的專案格式, 在您的應用程式中開啟每個 Visual C++ 專案, 然後再重新建立它。 std:: string 類別實作在這個版本是否為安全執行緒針對所述問題 如果您在的專案您的任何一個使用 DLL Run - Time 程式庫功能在您應用程式, 您必須散發新 Visual C++ Run - Time 元件 (例如 Msvci7x.dll、 Msvcp7x.dll 和 Msvcr7x.dll) 與重建應用程式。

請注意 您不需來散發給用戶端電腦要使用 Microsoft Visual C++ . NET Microsoft . NET Framework。

方法 2: 使用 Microsoft Visual C++ 6.0 使用備用 STL 從第三個廠商

依產品, 的整合細節上有所不同, 個別廠商提供支援。 一個來源為後續 STL 版本是 Dinkumware, Ltd, Microsoft Visual C++ 6.0 STL 授權, 該公司。 它會宣告, 它可以和現有的建置處理序整合。 如需詳細資訊, 和如需已知的錯誤與因應措施, 的清單請造訪下列 Dinkumware 網站:
www.dinkumware.com (http://www.dinkumware.com)
Microsoft 提供協力廠商的連絡資訊,以協助您找出技術支援。. 此連絡人資訊可能會變更不另行通知。 Microsoft 不保證精確度的協力廠商連絡人資訊。 本文提及的協力廠商產品是由與 Microsoft 無關的獨立廠商所製造。. Microsoft 不以暗示或其他方式,提供與這些產品的效能或可靠性有關的保證。.

其他可行方案

要解決在 Microsoft Visual C++ 6.0 STL Std::String 類別問題

如果不想升級至較新版本的 STL, 您可以嘗試修正標準的 Microsoft Visual C++ 6.0 安裝中的 std:: string 類別 - 執行緒安全性問題。 雖然有多執行緒 (Multi - Thread) 數中的 Microsoft Visual C++ 6.0 STL, 類別有關的問題是目前為止最常見且有問題的類別是 std:: string 類別。 下列步驟和替代解決方案均為權宜措施, 請確定應用程式是否正常運作, 而且量值提供時間來調查其他替代方案。 請考慮將這些指示建立新的程式碼路徑和行為可能是在整個應用程式中。 軟體原則廣泛部署之前, 先徹底測試以配合公司或個人的重建應用程式

停用參考計數 (Reference Counting 字串

每個替代的解決方案在本節所述的需要, 您首先停用參考計數的機制。 如果要停用參考計數 (Reference Counting, 您必須修改 < xstring > 標頭檔 (Header File) 並 _FROZEN 列舉常數設定為 0 。 在預設安裝, < xstring > 標頭檔 (Header File) 位於下列位置:
C: \Program files \Microsoft Visual Studio\VC98\Include
以便它看起來與下列類似將 _FROZEN 列舉常數變更為 0 在行為 62 < xstring > 標頭檔中:
enum _Mref {_FROZEN = 0}; // set to zero to disable sharing; original value 255
如果您遵循此建議, 但您重建所有的軟體, 使用這些標頭檔, std:: string 您的類別程式碼會更安全執行緒 (Thread - Safe。 有一些警告給該陳述式。 因此, 仔細閱讀下列替代解決方案指示。 在您停用參考計數 (Reference Counting 透過將 _FROZEN 列舉常數設定為 0 , < xstring > 標頭檔中使用的下列方法之一來解決這個問題。

方法 1: 使用 CRT 靜態連結只

修改專案設定在使用該 std:: string 類別要連結至靜態版本的 Microsoft Run - Time 程式庫 (CRT) 的所有您的專案。 如果您的專案也有啟用 使用 MFC 共用 DLL 中 設定您無法使用這種方法。 針對每個專案, 請依照下列步驟執行:
  1. 開啟專案。
  2. 在 [ 專案 ] 功能表, 按一下 [ 設定 ] 。
  3. 在 [ 組態 ] 清單, 按一下 [ Release 。
  4. 按一下 [ C / C++ ] 索引標籤, 及 [ 類別 ] 清單中的 [ 程式碼產生 。
  5. 在 執行階段程式庫 清單, 按一下 ) MT / (Multi-thread
  6. 在 [ 組態 ] 清單, 按一下 [ 偵錯 。
  7. 在 執行階段程式庫 清單, 按一下 Multi-thread 偵錯) MTd / (
  8. 如果還有其他組態在 [ 組態 ] 清單, 設定適當的 執行階段程式庫 選項為它們也
  9. 按一下 [ 確定 ] , 然後重建專案。
這項因應措施會確定所有您的程式碼藉由靜態連結至 MFC 包括整個多執行緒 Run - Time 程式庫, 使用修改過的 < xstring > 檔案版本。 有一個可能的問題就是您的最後一個程式碼大小會大於一個動態連結版本, 助益或許是

方法 2: 使用 CRT 動態連結

如果您的專案程式碼必須連結至執行階段程式庫 (CRT) 為 DLL, 您必須採取其他方法。 動態 CRT 連結是預設值為 DLL 專案。 一般來說需要動態連結至 CRT 對其他元件例如 MFC 或適用於您的應用程式, 是授權的協力廠商程式庫相依性。 如果您的唯一的相依性是 MFC, 使用 Static Library 中的使用 MFC 選項, 並套用 「 方法 1。 根據預設值, 當您在 Microsoft Visual C++ 6.0, 建立新專案專案使用 CRT 從 DLL。

動態 CRT 連結專案設定連結應用程式, 以對某些 std:: string 類別方法名為 Msvcp60.dll 預先建立的 Microsoft CRT DLL 中實作。 因為 Microsoft 藉由使用未修改的 < xstring > , 標頭檔 (Header File) 建立該 DLL 到, 您對本機複本的 < xstring > _FROZEN 常數變更為不接受對於超出該類別庫所呼叫函式。 這些包括函式例如 _Tidy() 和 assign() 在 Msvcp60.dll 檔案中為 < char > 及 < 簡短 > basic_string 類別的執行個體化所提供。 basic_string 類別是為 std:: string 類別基底。

若要在您的模組, 而不是在 Msvcp60.dll 檔案, Microsoft - 提供實作使用靜態 std:: string 類別實作請依照下列步驟執行:
  1. 在 . < xstring > 檔案, 將下列程式碼中找到的檔案結尾附近的註解。 如果要執行這項操作, 您可以將程式碼放在 # if 0 / # endif 區塊:
    #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. 其他的字串運算子, < 字串 > 標頭檔中定義, 而且它們也能夠透過 Msvcp60.dll 檔案包含在 CRT。 您注意到在結尾處, 有一系列的 「 extern 範本 _CRTIMP ... 」 字串 > < 檔案定義由 # ifdef _DLL 子句就像是在 < 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> > __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: 使用聰明的劈砍, 以避免連結問題

建立為 unsigned char , typedef, 並使用, 而不是現有 std:: string 類別 typedef。 typedef 可能要花標頭檔中之應用程式使用 std:: string 類別的原始程式檔 (Source File) 中所包含表單。 typedef 可能會出現與下列類似:
typedef std::basic_string<unsigned char> MyString;
#define string MyString
與這個類別搭配使用的任何字串常值必須轉換或處理為 unsigned char。 可能是以與簡易的實作, 程式碼大小的增加而且有較少的副作用 (Side Effect 連結。

方法 4: 使用自訂 Std::String DLL

這個選項取得您藉由在單一 DLL 放置 std:: string 類別實作優點的程式碼大小最小。 建立 DLL 專案匯出 std:: string 類別。 連結至此 DLL, 而不是到標準 Msvcp60.dll 檔案。 您必須一起使用您的應用程式轉散發這個新的 DLL。 這是一個進階選項。

其他相關資訊

在下列 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。 同時, 主執行緒指派新值到 A、 建立新字元緩衝區, 並置放在原始共用緩衝區設定為 1 參考次數。

若要建立新的字元緩衝區為新的工作分派到 A 1 啟動時, 在前一個共用的字元緩衝區, 然後遞減計數為 1 到 0, 其可辨識正參考次數 (Reference Count Thread1。 在同一時間, Thread2 也是 in the process of 指派至 B . B 共用, A 2 的字元緩衝區, 試著它增加為 2 Thread1 寫入參考計數 0 之前引發參考次數的 A 2, 字元緩衝區上。 現在參考計數為 0 而不為 1。 如果已經有同步存取參考計數器參考計數會是 0。

當將一個新的值指派給 A 2, Thread2 Thread2 看到參考次數為 0 並捨棄仍然參考 B, 原始的共用字元緩衝區。 保留字元緩衝區記憶體是現在可供其他應用程式中使用。 不過, std:: string B 仍保留 ULONG 32 的指標至字元緩衝區。 下列情況會造成損毀和損毀:
  • B 會嘗試釋放字元緩衝區。
  • B 嘗試讀取的字元緩衝區內容與即時資料已被覆寫, 由其他應用程式的程式碼。
  • 沒有嘗試來擴充或來修改字串。

?考

如需有關 Visual C++ 語言和編譯器問題, 詳細資訊請參閱 是包含在 V C++ 安全執行緒 (Thread - Safe STL 嗎 ? 主題, 請瀏覽下列 Microsoft 最有價值的專業人員 (MVP 他) 網站:
http://www.mvps.org/vcfaq (http://www.mvps.org/vcfaq)

這篇文章中的資訊適用於:
  • Microsoft Visual C++ 6.0 Service Pack 5
關鍵字:?
kbthreadsync kbprb KB813810 KbMtzh kbmt
機器翻譯機器翻譯
重要:本文是以 Microsoft 機器翻譯軟體翻譯而成,而非使用人工翻譯而成。Microsoft 同時提供使用者人工翻譯及機器翻譯兩個版本的文章,讓使用者可以依其使用語言使用知識庫中的所有文章。但是,機器翻譯的文章可能不盡完美。這些文章中也可能出現拼字、語意或文法上的錯誤,就像外國人在使用本國語言時可能發生的錯誤。Microsoft 不為內容的翻譯錯誤或客戶對該內容的使用所產生的任何錯誤或損害負責。Microsoft也同時將不斷地就機器翻譯軟體進行更新。如果您發現錯誤,並想要協助我們進行改善,請填寫本篇文章下方的問卷。
按一下這裡查看此文章的英文版本:813810? (http://support.microsoft.com/kb/813810/en-us/ )
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。