segunda-feira, 28 de junho de 2010

Atribuindo nomes às threads

Para quem trabalha com programação multi-thread sabe que a depuração do código é um tarefa bem complicada.
Percorrer o caminho inverso do fluxo de execução de uma tarefa que é intercalada por threads requer tempo de  habilidade.
O Delphi 2010 trás uma novidade interessante que promete ajudar nessa árdua tarefa, agora é possível nomear as threads.
Observe a imagem abaixo:



No Delphi 2010 foi introduzido o método NameThreadForDebugging na classe TThread e é utilizado tanto para nomear quanto para renomear uma thread específica.
Sua assinatura trás dois parâmetros: AThreadName e AThreadID.
O primeiro é o nome que será dado à thread e o segundo parâmetro é o ID da thread a ser nomeada. No caso do segundo parâmetro ser omitido, ou ser -1, a operação será realizada sob a corrente thread que está rodando.
Outro detalhe é que o nome atribuído é do tipo AnsiString, ou seja, pode ser necessário um typecast explícito.

Vamos ver na prática como o recurso é aplicado.

procedure TMyThread.Execute;
var
  lThreadName: AnsiString;
begin
  {$IFDEF VER210}
  lThreadName := AnsiString(Self.ClassName + '_' + IntToStr(FCount));
  NameThreadForDebugging(lThreadName);
  {$ENDIF}

  while not Terminated do
  begin

    Sleep(300);
  end;
end;

Não se esqueça que esse método só está disponível no Delphi 2010 e, provavelmente, nas versões superiores. No caso do código fonte ser utilizado em versões anteriores, use utilize a diretiva de compilação {$IFDEF VER210} para evitar erro de compilação/compatibilidade.

Apesar desse recurso ser introduzido somente na versão 2010, existe um método que pode ser aplicado para as versões anteriores.

{$IFDEF MSWINDOWS}

procedure SetDebbugerThreadName(const AThreadName: AnsiString; const AThreadID: LongWord = $FFFFFFFF);
type
  TThreadNameInfo = record
    FType:     LongWord;  // Deve ser sempre 0x1000
    FName:     PAnsiChar; // Ponteiro para o nome da thread
    FThreadID: LongWord;  // Thread ID (-1 indica a corrente thread)
    FFlags:    LongWord;  // Não utilizado, deve ser zero.
  end;
var
  lThreadInfo: TThreadNameInfo;
begin
  lThreadInfo.FType     := $1000;
  lThreadInfo.FName     := PAnsiChar(AThreadName);
  lThreadInfo.FThreadID := AThreadID;
  lThreadInfo.FFlags    := 0;

  try
    RaiseException($406D1388, 0, SizeOf(lThreadInfo) div SizeOf(LongWord), @lThreadInfo);
  except
  end;
end;

{$ENDIF}

O uso dessa função tem exatamente o mesmo efeito que o método NameThreadForDebugging.

Um exemplo completo utilizando ambas formas pode ser baixada aqui.

Nenhum comentário: