ID do artigo: 813810 - Última revisão: terça-feira, 1 de junho de 2004 - Revisão: 1.0

Classe Std::string stl provoca falhas e memória dano em máquinas com vários processadores

Dica do SistemaEste artigo aplica-se a um sistema operativo diferente do que está a utilizar. Foi desactivado o conteúdo do artigo, que pode não ser relevante para si.

Nesta página

Expandir tudo | Recolher tudo

Sintomas

Quando você compilar aplicativos no 6.0 Microsoft Visual C++ que usam o Biblioteca de Modelos Padrão fornecido (stl), memória dano pode ocorrer, ou seu computador pode parar de responder. Esses ocorrer sintomas mais freqüentes em computadores com vários processadores. Anteriormente, o mesmo codificar talvez tenha trabalhado sem esses problemas em um único - computador processador. Quando você examinar o segmento faulting em um depurador, geralmente você Consulte a falha em uma função gerenciamento memória. Com freqüência você ver os métodos classe basic_string Char. <.. > no rastreamento de pilha. Porque memória dano é também um sintoma, falhas podem aparecer em áreas que são não relacionado para processamento seqüência de caracteres.

O seguinte são exemplos de pilha rastreamentos onde esse problema foi a causar de um falhar:
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]
...

Causa

O Biblioteca de Modelos Padrão (stl) que está incluído no Microsoft Visual C++ 6.0 não é seguro para aplicativos com vários segmentos. Em particular, as implementações da classe Std::string dependem a classe modelo basic_string... < > . A referência classe modelo basic_string... < > conta cópias de uma reserva caractere ocultos. A classe modelo basic_string... < > armazena a contagem em um 8 - bit sem assinatura char. O seguinte ocorrer problemas gerais após esta implementação:
  • A classe modelo basic_string... < > não protege a contagem mecanismo com a sincronização é exigido para segmentos em computadores com vários processadores para executar ao mesmo tempo o. Codificar stl com vários segmentos que é execução em único - processador computadores evita esse emitir porque apenas um segmento é executado em um tempo, e memória leituras ou gravações em números inteiros são concluídas antes de outro segmento interrupção.
  • Gravar em um Std::string classe em um segmento pode danificar a leitura de um copiar a classe Std::string , such as um criado por atribuída, em outro segmento de. A copiar supposed compartilha a mesma reserva caractere ocultos.
  • Dano seqüência pode ocorrer onde um ponteiro ou referência a uma classe Std::string está compartilhada entre segmentos. Geralmente, é a responsabilidade do programador para evitar essa situação.

Resolução

Você deve reconstruir o aplicativo depois que você fizer a stl thread-Safe. É o método preferencial para obter um stl thread-Safe atualizar o stl para uma versão mais recente que se baseie na atual Visual C++ padrão. No entanto, o stl que se baseie na atual não Visual C++ padrão é idêntico ao stl que estava disponível no tempo que Microsoft Visual C++ 6.0 foi lançada como um novo produto. No entanto, atualizar para uma nova versão pode ser trivial depending on as funções stl que seu aplicativo usa. Para obter novas versões do stl isenta de segmentos, use um do seguinte métodos:

Microsoft Método 1: Usar Visual C++.NET (versões 7.0 e posterior)

Abrir cada projeto Visual C++ no seu aplicativo, permitir o projeto para converter automaticamente ao novo formato projeto, e depois reconstrui-lo. A implementação classe Std::string nesta versão é isenta de segmentos para o problema descrito. Se você usar o recurso run-time library DLL em seu qualquer um dos projetos em seu aplicativo, você deve distribuir o novo executar Visual C++ - componentes tempo (como Msvci7x.dll, Msvcp7x.dll, e Msvcr7x.dll) com seu aplicativo recriado.

Observação você não tem que distribuir o Microsoft.NET Framework aos computadores cliente para usar Microsoft Visual C++.NET.

Método 2: Usar 6.0 Microsoft Visual C++ com uma substituição de um terceiros stl

Os detalhes da integração variar por produto, e o individual fornecedores oferecem suporte. Uma origem para uma versão stl sucessora é Dinkumware, Ltd., a empresa onde Microsoft licenças a Visual C++ 6.0 STL. Ele é solicitado que ele pode integrar com processos compilar existente. Para obter mais informações, e para uma lista de erros conhecidos e soluções alternativas, visite o seguinte site Dinkumware:
www.dinkumware.com (http://www.dinkumware.com)
A Microsoft fornece informações para contato com terceiros para ajudá-lo a encontrar o suporte técnico.. Este informações de contato podem alteração sem aviso. A Microsoft não garante a precisão destas informações de contato de terceiros.. Os produtos de terceiros mencionados neste artigo são fabricados por empresas que são independentes da Microsoft.. Microsoft torna nenhuma garantia, implícita ou caso contrário, sobre o desempenho ou confiabilidade um desses produtos.

Como Contornar

Contornar a emitir classe Std::string em Microsoft Visual C++ 6.0 STL

Se você não atualizar para uma nova versão do stl, você pode tentar corrigir a emitir thread-Safety classe Std::string no padrão Microsoft Visual C++ 6.0 instalação. Embora há questões multi-Threading com vários das classes na stl 6.0 Microsoft Visual C++, by far the Most comum e classe problemático é a classe Std::string . O seguinte etapas e soluções alternativas são medidas paliativa para certificar-se de que um aplicativo está funcionando corretamente, e as medidas fornecem tempo para investigar outras alternativas. Considere estas instruções que irá criar novos caminhos codificar e comportamento em talvez todo o aplicativo inteiro. Testar completamente o aplicativo recriado in accordance with de uma empresa ou um individual do diretivas software antes implantação amplamente.

Desativar contagem de referência seqüência de caracteres

Cada das soluções que está documentado nesta seção requer que você primeiro desativar a referência - contagem mecanismo. Para desativar contagem de referência, você deve modificar o arquivo cabeçalho xstring < > e definir o _FROZEN constante enumeração para 0 . Em instalações usar como padrão, o arquivo cabeçalho xstring < > está no seguinte localidade:
C:\Program Files\Microsoft Visual Studio\VC98\Include
Altere a constante enumeração _FROZEN para 0 no arquivo de cabeçalho xstring < > na linha 62 para que fique semelhante ao seguinte:
enum _Mref {_FROZEN = 0}; // set to zero to disable sharing; original value 255
Se você seguir essa recomendação, e você reconstruir seu codificar classe Std::string todos os software que usa esses arquivos cabeçalho, será mais isenta de segmentos. Há algumas advertências para essa declaração. Portanto, ler o seguinte solução alternativa instruções cuidadosamente. Depois que você contagem de referência desativar por configuração a constante enumeração _FROZEN como 0 no arquivo cabeçalho xstring < > , use um do seguinte métodos para trabalho em torno esse problema.

Método 1: Usar CRT vinculação estática somente

Modificar as configurações em todos os seus projetos que usam a classe Std::string para projeto link para a versão do run-time library Microsoft (CRT) estático. Não será possível usar essa abordagem se seu projeto também tem o Use MFC em um Shared DLL configuração habilitado. Para cada projeto, siga estas etapas:
  1. Abra o projeto.
  2. O menu Project , clique em Configurações .
  3. A lista Configurações , clique em release .
  4. Clique no C / guia C++, e seguida, clique geração de código na lista a categoria .
  5. A lista biblioteca de tempo de execução , clique em Multi-thread (/ MT) .
  6. A lista Configurações , clique em Debug .
  7. A lista biblioteca de tempo de execução , clique em depuração Multi-thread (/ MTd) .
  8. Se há outras configurações na lista Configurações , defina o apropriado opção biblioteca de tempo de execução para que eles também.
  9. Clique OK , e depois reconstruir o projeto.
Este solução alternativa certifica que seu codificar todos os usa a versão modificada do arquivo xstring < > por vinculação estática para o run-time library com vários segmentos inteiros, incluindo MFC. Um possível problema é que o tamanho final codificar será maior que uma versão dinamicamente vinculado, talvez enormously caso.

Método 2: Usar dinâmico CRT vinculação

Se seu projeto codificar deve link para a biblioteca tempo de execução (CRT) como um DLL, você deve fazer uma abordagem diferente. Vinculação CRT dinâmica é o configuração padrão para projetos DLL. Dependências em outros componentes, como MFC ou terceiro - parte bibliotecas que são licenciadas para uso com seu aplicativo, geralmente exigem dinâmico ligação para o CRT. Se somente a dependência é MFC, você poderá usar a opção Use MFC em um Static Library , e aplicar Method 1. Por padrão, quando você cria um novo projeto no Microsoft Visual C++ 6.0, o projeto usa o CRT de um DLL.

O dinâmico projeto vinculação CRT configuração vincula seu aplicativo para implementações para alguns métodos classe Std::string no Microsoft CRT DLL pré-criado que é nomeado Msvcp60.dll. Porque Microsoft compilado que DLL usando o arquivo cabeçalho não modificada xstring < > , a alteração para a constante _FROZEN que você fez para o local copiar de xstring < > não é aceito para funções que são chamado out of essa biblioteca. Inclui funções such as _Tidy(), e Assign() que são fornecidos no arquivo Msvcp60.DLL para o <char> e <short> instanciações da classe basic_string. A classe basic_string é o de base para a classe Std::string .

Para usar implementações estáticas da classe Std::string em seus módulos em vez das implementações fornecidos pela Microsoft no arquivo Msvcp60.dll, siga estas etapas:
  1. No arquivo xstring < >, comentar fora a seguinte codificar que foi encontrada próximo ao final do arquivo. Para fazer isso, você pode incluir a codificar em um bloco endif 0/# #if :
    #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. Outros operadores seqüência de caracteres são definidos no arquivo cabeçalho seqüência de caracteres < >, e elas também são incluídas na CRT através do arquivo Msvcp60.dll. Você aviso no final do arquivo <string> que há uma série de definições " _CRTIMP modelo externo... " que são protegidas pela cláusula _DLL #ifdef exatamente como no arquivo <xstring>. Comentário todas essas definições saída também:
    #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. Salvar as modificações a esses arquivos, e depois reconstruir todos os projetos no aplicativo que usam o stl. Se seu projeto declara uma classe __declspec(dllexport) classe , e essa classe possui membros do tipo Std::string , você verá avisos C4251. Porque é a codificar todos os compilado com a classe Std::string agora estaticamente vinculado, você pode ignorar esses avisos. Notação para explicitamente desativar esses avisos, use o seguinte:
    #pragma warning(disable: 4251)
Essa abordagem equilibra o uso de funções MFC e CRT Other Than a classe Std::string a partir de DLLs. Há um aumento pequeno no tamanho codificar em cada um dos seus módulos que usam a classe Std::string .

Método 3: usando um penetrar inteligente para evitar problemas vinculação

Criar um typedef para sem assinatura char , e o uso do que em vez da typedef classe Std::string existente. O typedef pode levar uma forma que está incluída em um arquivo de cabeçalho nos arquivos o aplicativo de origem que usam a classe Std::string . O typedef pode parecer com o seguinte:
typedef std::basic_string<unsigned char> MyString;
#define string MyString
Qualquer literais seqüência de caracteres que são usados com essa classe devem ser CAST ou processados como sem assinatura char. Pode haver um aumento no tamanho codificar versus facilidade de implementação, e há efeitos colaterais vinculação menos.

Método 4: usando um personalizado Std::string DLL

Esta opção obtém você a beneficiar de menor tamanho codificar colocando as implementações classe Std::string em um único DLL. Criar um projeto DLL que exporta a classe Std::string . Link para esse DLL INSTEAD OF para o padrão arquivo Msvcp60.dll. Você deve redistribuir este novo DLL together with seu aplicativo. Este é um avançado opção.

Mais Informações

O seguinte C++ codificar exemplos demonstrar um cenário que pode ocorrer quando houver uma falta de sincronização:
...
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";
}
Neste exemplo, Thread1 faz um copiar a entrada de argumento, e raises a contagem de referência na reserva compartilhada caractere como 1. Enquanto ele está funcionando, Thread2 também torna um copiar da sua entrada argumento, e raises a contagem de referência para 2. Enquanto isso, o segmento principal atribui um novo valor a, cria uma nova reserva caractere, e ignora a contagem de referência no original compartilhada reserva como 1.

Inicia Thread1 para criar uma nova reserva caractere para a nova atribuída para A1, reconhece um contagem de referência positiva em sua anterior reserva caractere compartilhada, e clique decrementa que contagem por 1 to 0. No mesmo tempo, Thread2 é também no processo de atribuída aos compartilhamentos B. B o buffer de caracteres de A2, e gera a contagem de referência na reserva caractere de A2, tentam incremento-lo 2 Thread1 grava um 0 para o contagem de referência antes-lo 2 antes Thread1 grava um 0 para o contagem de referência.. A contagem de referência é agora 0 em vez de 1. A contagem de referência teria sido 0 se acessar à referência contador foi sincronizado.

Quando Thread2 atribui um novo valor para A2, Thread2 vê a contagem de referência de 0 e descarta o original compartilhada buffer de caracteres que faz referência ainda B. A memória que mantida a reserva caractere é agora disponível para outros usa no aplicativo. Ainda no entanto, Std::string B contém um ponteiro para a reserva caractere. O seguinte cenários causar dano e falhas:
  • B tentará livre a reserva caractere.
  • B tentará ler o conteúdo de reserva do caractere que tiver sido substituídas com dados ao vivo por outros código do aplicativo.
  • Há tentativas para estender ou para modificar o seqüência de caracteres.

Referências

Para obter mais informações sobre linguagem Visual C++ e compilador problemas, consulte o É o stl incluído no VC ++ isenta de segmentos? tópico no seguinte site Microsoft Most Valuable Professional (MVP):
http://www.mvps.org/vcfaq (http://www.mvps.org/vcfaq)

A informação contida neste artigo aplica-se a:
  • Microsoft Visual C++ 6.0 Service Pack 5
Palavras-chave: 
kbthreadsync kbprb KB813810 KbMtpt kbmt
Tradução automáticaTradução automática
IMPORTANTE: Este artigo foi traduzido por um sistema de tradução automática (também designado por Machine Translation ou MT), não tendo sido portanto traduzido ou revisto por pessoas. A Microsoft possui artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais, com o objetivo de oferecer em português a totalidade dos artigos existentes na base de dados de suporte. No entanto, a tradução automática não é sempre perfeita, podendo conter erros de vocabulário, sintaxe ou gramática. A Microsoft não é responsável por incoerências, erros ou prejuízos ocorridos em decorrência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza atualizações freqüentes ao software de tradução automática (MT). Caso venha a encontrar erros neste artigo e queira colaborar no processo de aperfeiçoamento desta ferramenta, por favor preencha o formulário existente na parte inferior desta página. Obrigado.
Clique aqui para ver a versão em Inglês deste artigo: 813810  (http://support.microsoft.com/kb/813810/en-us/ )