Pergunta sobre delphi, destructor, tthread – Uma maneira correta de destruir um objeto TThread

15

Essa pergunta pode parecer trivial, mas espero que você não a ignore.
Antes de destruir um objeto TThread, geralmente é necessário esperar até que o thread que chamou o método TThread.Execute () seja concluído, pois somente assim podemos ter certeza de que, por exemplo, os objetos destruídos dentro do destruidor da classe não serão mais acessados. Portanto, é necessário chamar Terminate para definir o sinalizador Terminated que o thread deve verificar para saber se deve sair ou não e, em seguida, chamar o método WaitFor ().

Porque o segmento pode ser suspenso, eu acho que é bom para retomá-lo antes de chamar WaitFor, caso contrário, o segmento de chamada seria deadlocked. E porque o thread pode ser suspenso várias vezes, deve ser retomado o mesmo número de vezes, certo?

while Suspended do
  Resume;

Se o encadeamento foi criado suspenso, não precisamos nos preocupar que o método TThread.Execute () será chamado quando retomarmos o encadeamento apenas para finalizá-lo - não será (corrija-me se estiver errado).

O que afirmei sugere o uso das seguintes linhas de código para cada objeto TThread sendo liberado:

MyThread.Terminate;
while MyThread.Suspended do
  MyThread.Resume;
MyThread.WaitFor;
MyThread.Free;

Infelizmente, quando destruímos nosso aplicativo que criou vários encadeamentos, a gravação desse pedaço de código para cada objeto TThread sendo destruído desnecessariamente torna o código muito longo e talvez até mesmo opaco.

Portanto, cheguei a uma conclusão de que tudo isso poderia ser colocado dentro de um destruidor overriden da classe TThread, graças ao qual seria suficiente chamar MyThread.Free (ou MyThread.Terminate se MyThread.FreeOnTerminate estivesse definido) sem se importar se o destruído objeto é um objeto TThread ou não:

destructor TMyThread.Destroy;
begin
  //if FreeOnTerminate, the calling thread cannot wait for itself
  if GetCurrentThreadId <> ThreadId then
  begin
    Terminate;
    while Suspended do
      Resume;
    WaitFor;
  end;

  {free all objects created in this class}

  inherited Destroy;
end;

Perdoe-me fazendo uma pergunta tão básica. Gostaria, no entanto, de conhecer suas opiniões sobre esse caminho - espero uma maneira universal - de destruir objetos TThread. Eu faço essas perguntas, pois aprendi com os códigos dos meus colegas de trabalho que eles costumavam usar o primeiro exemplo de código para destruir tais objetos, mas nunca costumavam verificar se os threads que estavam sendo aguardados não estavam suspensos, o que eu considerava um pouco perigoso pode ser suspenso em algum lugar no código. Por isso, tentei encontrar uma maneira universal de destruir os objetos dessa classe que tornaria o código mais claro e seguro. Espero não ter piorado - o que você acha?

Obrigado por suas sugestões com antecedência.

Um lembrete das palavras do Lord Polonius de Hamlet: Brevidade é a alma da inteligência Argalatyr

Sua resposta

2   a resposta
8

Muito do que a sua sugestão já é realizada no destruidor TThread.Destroy, e invocar TMyThread.free fará exatamente o que você está sugerindo. Para limpar quaisquer objetos pertencentes à classe de encadeamento, você pode executar isso no evento OnTerminate, que será chamado como parte da lógica de encerramento do encadeamento.

@ Mariusz: Tente codificar sem Suspend () e Resume (), e você não terá esse problema. Esses métodos podem ser muito problemáticos, como você se encontrou, e há sempre uma maneira de codificar sem eles. Deixe o thread bloquear em uma ou várias primitivas de sincronização fornecidas pelo sistema. Ou use loops de mensagens, existem várias respostas com informações sobre multithreading aqui em SO em [delphi]. mghie
Sim, é verdade, no entanto, verifiquei que somente quando o thread é criado suspenso é retomado quando o destruidor é chamado. Quando eu suspenso durante o seu trabalho, chamando o destrutor sem retomar isso causará um impasse. E é por isso que eu estava procurando uma maneira universal de destruir objetos TThread, que seria bom para qualquer classe que herda de TThread. Mariusz Schimke
8

Não existe um modo universal de interromper um thread, assim como não há um modo universal de interromper (graciosamente) um processo. Cada um é diferente.

Para alguns tópicos, é suficiente definirTerminated propriedade através doTerminate método. Outros tópicos, no entanto, chamam funções comoGetMessage ouMsgWaitForMultipleObjects, que irá bloquear até que algo aconteça, como uma mensagem chegando ou um identificador de kernel sendo sinalizado.TThread.Terminate não pode fazer qualquer uma dessas coisas acontecer, por isso não pode fazer com que esses tópicos parem de ser executados. Quando eu escrevi tópicos como esses, eu forneci minhas próprias funções para notificá-los para parar de correr. Posso ligarPostThreadMessage para forçar uma mensagem na fila do encadeamento, ou eu poderia sinalizar o evento que a classe de encadeamento forneceu para notificá-lo de um pedido para finalizar.

Não se preocupe em retomar um thread suspenso. Você realmente não deveria estar suspendendo eles de qualquer maneira. A única forma segura de suspender um encadeamento é que o encadeamento suspenda a si mesmo e, depois de ter isso, é garantido que você tenha pelo menos dois encadeamentos controlando quando o encadeamento é executado: o próprio encadeamento para suspendê-lo e pelo menos um outro. thread para retomar novamente. Um thread deve estar no controle de sua própria execução.

Seria ótimo seTThread.Terminate eram virtuais. Em seguida, cada classe de encadeamento poderia fornecer uma maneira personalizada de se notificar de que deveria parar de executar. Alguns poderiam apenas definirTerminatede outros podem postar mensagens, sinalizar eventos ou fazer o que mais precisarem. No entanto, como é, o método não virtual não funciona bem com threads que gastam muito tempo esperando por outras coisas. A maneira atual só funciona para encadeamentos que podemvotação seusTerminated propriedades.

Alguns tópicos têm suasFreeOnTerminate conjunto de propriedades. Para esses tópicos, seu código não é seguro. Tecnicamente, não é seguro ligarqualquer métodos em tais objetos desde que o segmento pode terminar a qualquer momento. Mas mesmo que você saiba que o segmento ainda está em execução e o objeto de thread ainda existe, o objeto definitivamente parará de existir algum tempo após a chamada paraTerminate. Você não pode ligarWaitFor em um objeto thread free-on-terminate, e você definitivamente não pode chamarFree.

Perguntas relacionadas