Artigo: 163449 - Última revisão: quarta-feira, 21 de Fevereiro de 2007 - Revisão: 4.2

Utilização de memória local threads num procedimento armazenado expandido

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 | Reduzir tudo

Sumário

O armazenamento local de thread (TLS) pode ser um assunto muito difícil. Quando trabalha num thread de agrupamento de ambiente, é muito importante que compreenda as consequências da TLS e as interacções de thread.

Recomenda-se muito, evitar TLS num thread agrupamento ambiente como o Microsoft SQL Server. No entanto, se deve utilizar TLS, verifique cuidadosamente ler este documento e consulte a documentação online no SDK do Win32 para as funções principais TLS e DLL.

Além disso, o __declspec(thread) directiva de compilador não é suportada num procedimento armazenado expandido. Quando ocorre LoadLibarary, a definição __declspec(thread) não está inicializada correctamente. Consulte "Windows Advanced" por Jeffrey Ricter para obter mais detalhes.



NOTA: Qualquer extensão do SQL Server, incluindo procedimentos armazenados estendidos, nunca poderá fazer uma chamada para DisableThreadLibraryCalls().

Mais Informação

Conforme é documentado, a função de TlsAlloc devolve um valor de índice é utilizado em chamadas de função para TlsSetValue e TlsGetValue. A documentação sugere que chamar a função TlsAlloc na sua função DLLMain quando ul_reason_for_call = DLL_PROCESS_ATTACH. Por predefinição, DLL_PROCESS_ATTACH é chamado quando a DLL é carregada inicialmente.

Em DLL_PROCESS_ATTACH e DLL_THREAD_ATTACH, deve atribuir memória e chamar TlsSetValue. Trata-se especificamente onde o thread de agrupamento começa a causar alguns problemas no cenário.

Segue-se um cenário de exemplo que se aplica a qualquer aplicação que thread de agrupamento. Este exemplo utiliza a aplicação do Microsoft SQL Server.

Seguem-se algumas noções básicas para o exemplo que é necessário compreender. Todas estas estão documentadas mais detalhadamente na documentação do Win32 SDK em função DLLMain.
  • Quando uma DLL é carregada, ele chama DLL_PROCESS_ATTACH.
  • Todos os segmentos subsequentes (gerados depois da DLL é carregada) chamada DLL_THREAD_ATTACH.
  • Threads actualmente em execução (geradas antes da DLL está carregada) não chamar DLL_THREAD_ATTACH.
  • Todos os threads chamar DLL_THREAD_DETACH, mesmo que nunca chamados DLL_THREAD_ATTACH.
Por exemplo, suponha que o gráfico seguinte mostra a ordem e a utilização do conjunto de threads de trabalho do SQL Server:
   Thread     ATTACH CALLED     COMMAND     USER
------------------------------------------------ 
     1              NO           select      Joe
     2             YES          xp_test     Mary
     3             YES           select     Adam

     1              NO          xp_test     Lynn
				

João inicia uma instrução select de execução longa. Ninguém tem utilizado xp_test expandido o procedimento armazenado, por isso não é possível para o processo DLL_ATTACH a ser chamado.

Durante a execução seleccione de José, Maria executa xp_test. O thread de agrupamento mecanismo determina que um novo thread deve ser gerado a pedido Maria serviço. SQL Server, em seguida, chama a função LoadLibrary para carregar o ficheiro Xproc.dll. Ao fazê-lo, thread 2 é o primeiro thread para anexar a DLL para DLL_PROCESS_ATTACH é designado por. Tal como discutido anteriormente, TlsAlloc pode ser chamado para inicializar o valor de índice do procedimento armazenado expandido TLS.

Durante a execução de João e a de Maria comandos, ADAM submete o seu próprio seleccione. Novamente, um novo thread é gerado para processar o pedido de ADAM. Uma vez que é gerado thread 3 depois do LoadLibrary teve lugar, thread 3 chamadas DLL_THREAD_ATTACH. Conforme é documentado, é onde deve atribuir a memória para thread 3 e chamar TlsSetValue.

Agora, suponha que seleccione José concluída, thread de trabalho 1 é livre para utilização. Lynn submete o comando xp_test e é atribuído o thread 1. Thread 1 chamadas nunca DLL_PROCESS_ATTACH ou DLL_THREAD_ATTACH, porque o thread foi gerado antes do LoadLibrary foi chamado.

Neste exemplo, pode ver que qualquer tentativa por thread 1 aceder à memória TLS com TlsGetValue resultará num apontador NULL devolvido. Se o procedimento armazenado expandido não está escrito correctamente para verificar se esta condição, encontrará uma violação de acesso (AV) quando tenta escrever o endereço de NULL.

Seguem-se vários pontos que necessitam de ser mencionado sobre TLS e o thread de agrupamento.

Se estiver a utilizar TLS num ambiente de agrupamento, terá de verificar sempre TlsGetValue para um valor de retorno nulo. Quando chegar um NULL, correctamente tem de atribuir memória e chamada TlsSetValue para processar os threads que onde gerada antes do LoadLibrary teve lugar.

Outro truque directamente não resolvido a documentação é que irá atribuir a memória TLS na função DLLMain para cada thread gerado depois LoadLibarary, mesmo que o thread nunca utiliza as funções na DLL. O exemplo acima mostra este com thread 3. -Executado apenas uma selecção, mas esta chamada DLL_THREAD_ATTACH atribuída memória TLS para o procedimento armazenado expandido.

Se colocar esta estrutura de um thread de agrupamento de ambiente, poderá atribuir a memória que nunca pode ser utilizada.

Segue-se a melhor forma para optimizar a atribuição de memória:
  1. Em DLL_PROCESS_ATTACH, obter o valor de índice TLS correcto a partir do TlsAlloc.
  2. Não atribuir memória DLL_PROCESS_ATTACH ou DLL_THREAD_ATTACH.
  3. Criar uma função genérica para obter o valor TLS; se for NULL, atribui-lo correctamente e chamar TlsSetValue.

    Este passo restringe a atribuição de memória para os threads que realmente utilização-la. Isto pode reduzir o tempo sobrecarga e de arranque de threads de trabalho significativamente. Também cria em redundância para processar correctamente os módulos que foram gerados antes do LoadLibrary teve lugar.
  4. Como DLL_PROCESS_DETACH e DLL_THREAD_DETACH sempre denominam-se, Liberte memória esses processos.
A apenas outra coisa ainda pode ser confusa for chamada TlsAlloc. Ler a documentação, poderá parecer que o valor de índice TLS devolvido é um valor global. É um valor global, mas cada chamada para TlaAlloc devolve um valor de índice diferente. Isto permite que dois procedimentos armazenados estendidos ter seu próprio valor de índice TLS e processar correctamente os seus próprios dados TLS sem afectar os outros.

Segue-se um procedimento armazenado expandido que mostra o comportamento descrito no exemplo:
   // 
   //    1. Start SQL Server from the command prompt to see the output:
   //       ...\Mssql\Binn\Sqlservr -c
   // 
   //    2. Run Xproctst.cmd to show the behavior.
   // 
   #include "windows.h"
   #include "stdio.h"
   #include "srv.h"
 
   #define           TLS_FAIL    0xFFFFFFFF

   DWORD          dwTlsIndex     =  TLS_FAIL;
   DWORD          dwCounter      =  0;
   CRITICAL_SECTION  csSync;

   // 
   //    Cleanup TLS memory
   // 
   void vCleanUpTls(void)
   {
      char *   strData     =  NULL;
      if(TLS_FAIL != dwTlsIndex)
      {
         strData = TlsGetValue(dwTlsIndex);
         if(NULL != strData)
         {
            free(strData);
            printf("\n >>> Tls memory released by thread %ld",
   GetCurrentThreadId());
         }
      }
   }
   // 
   //    Setup the TLS pointer
   // 
   void vSetUpTls(void)
   {
      char *      strData  =  NULL;
      if(TLS_FAIL == dwTlsIndex)
      {
         dwTlsIndex = TlsAlloc();
      }
      // 
      //    Are we ready to go
      // 
      if(TLS_FAIL != dwTlsIndex)
      {
         strData = (char *) calloc(256,1);
         if(strData)
         {
            printf("\n >>> Tls memory allocated by thread %ld",
   GetCurrentThreadId());
            if(TRUE == TlsSetValue(dwTlsIndex, strData))
            {
               // 
               //    Protect the counter.
               // 
               EnterCriticalSection(&csSync);
               sprintf(strData, "Counter = %ld", ++dwCounter);
               LeaveCriticalSection(&csSync);
            }
            else
            {
               printf("\n >>> *** Serious error *** TlsSetValue
               failed.\n");
            }
         }
         else
         {
            printf("\n >>> *** Serious error *** can not allocate
            memory.\n");
         }
      }
      else
      {
         printf("\n >>> *** Serious error *** TlsAlloc failed.\n");
      }
   }
   // 
   //    DLLMain
   // 
   BOOL APIENTRY DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID
   lpReserved)
   {
      char  strInfo[256]   =  "";
      switch(ul_reason_for_call)
      {
         case DLL_PROCESS_ATTACH:
            InitializeCriticalSection(&csSync);
            vSetUpTls();
            sprintf(strInfo, "\n >>> DLL_PROCESS_ATTACH Thread: %ld",
   GetCurrentThreadId());
            break;
        case DLL_THREAD_ATTACH:
            vSetUpTls();
            sprintf(strInfo, "\n >>> DLL_THREAD_ATTACH Thread: %ld",
   GetCurrentThreadId());
            break;
         case DLL_PROCESS_DETACH:
            vCleanUpTls();
            if(TLS_FAIL != dwTlsIndex)
               TlsFree(dwTlsIndex);
            DeleteCriticalSection(&csSync);
            sprintf(strInfo, "\n >>> DLL_PROCESS_DETACH Thread: %ld",
   GetCurrentThreadId());
            break;
         case DLL_THREAD_DETACH:
            vCleanUpTls();
            sprintf(strInfo, "\n >>> DLL_THREAD_DETACH Thread: %ld",
   GetCurrentThreadId());
            break;
      }
      printf(strInfo);
      return TRUE;
   }
   // 
   //    DLL function in the extended stored procedure to show TLS trap
   // 
   __declspec(dllexport) SRVRETCODE xp_Tls(SRV_PROC *pSrvProc)
   {
      char  strInfo[256]   =  "";
      char *   strData        =  NULL;
      sprintf(strInfo, "\n >>> Invoking xp_Tls on Thread: %ld",
   GetCurrentThreadId());
      printf(strInfo);
      if(TLS_FAIL != dwTlsIndex)
      {
         strData = TlsGetValue(dwTlsIndex);
         if(NULL != strData)
         {
            sprintf(strInfo, "\n >>> %s", strData);
            printf(strInfo);
         }
         else
         {
            printf("\n >>> *** Serious error *** TlsGetValue returned NULL,
   thread pooling not handled correctly.\n");
         }
      }
      return 1;
   }
				

Mais Informação

SQL Server 7.0 e SQL Server 2000 Fibers

É vivamente desencorajar utilizar TLS e não suportam a utilizar o TLS no modo de Fiber. No modo de Fiber, o thread físico pode ser alterado por muitos motivos, efectuar qualquer TLS não seguros.

A informação contida neste artigo aplica-se a:
  • Microsoft SQL Server 2000 Enterprise Edition
  • Microsoft SQL Server 7.0 Standard Edition
  • Microsoft SQL Server 6.0 Standard Edition
  • Microsoft SQL Server 6.5 Standard Edition
Palavras-chave: 
kbmt kbhowto kbother kbprogramming kbusage KB163449 KbMtpt
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 revisto ou traduzido por humanos. A Microsoft tem artigos traduzidos por aplicações (MT) e artigos traduzidos por tradutores profissionais. O objectivo é simples: oferecer em Português a totalidade dos artigos existentes na base de dados do suporte. Sabemos no entanto que a tradução automática não é sempre perfeita. Esta pode conter erros de vocabulário, sintaxe ou gramática? erros semelhantes aos que um estrangeiro realiza ao falar em Português. A Microsoft não é responsável por incoerências, erros ou estragos realizados na sequência da utilização dos artigos MT por parte dos nossos clientes. A Microsoft realiza actualizações frequentes ao software de tradução automática (MT). Obrigado.
Clique aqui para ver a versão em Inglês deste artigo: 163449  (http://support.microsoft.com/kb/163449/en-us/ )