5 votes

Comment arrêter un fil avant qu'il n'ait fini de fonctionner ?

J'ai un thread appelé TMyThread et j'ai remplacé la procédure d'exécution comme ceci :

 procedure TMyThread.Execute;
 Begin
     repeat
     //Some Work 
     Sleep(5000);
     //Some Work 2;
     Sleep(5000); 
     until FActive=False
 End;

Dans le formulaire principal, j'ai un bouton appelé "Détruire mon fil". Je veux détruire mon fil mais le problème est que mon fil ne sera détruit que s'il a terminé son travail. Je veux détruire mon fil même s'il n'a pas terminé son travail. Comment puis-je faire cela ?

23voto

David Heffernan Points 292687

Votre fil Execute doit régulièrement vérifier l'état de l'objet de la méthode Terminated propriété. Et si c'est True puis le fil Execute doit se terminer.

Donc, un typique Execute pourrait ressembler à ceci :

procedure TMyThread.Execute;
begin
  while not Terminated do
    DoNextPieceOfWork
end;

On dirait que votre fil a sa propre FActive qui effectue la même tâche. Le problème avec cela est que TThread n'est pas au courant. Donc vous devriez vous débarrasser FActive et utiliser plutôt le mécanisme intégré.

Lorsque vous appelez Free sur le fil, il appellera Terminate . Cela met Terminated à être True . Ensuite, il attend que la méthode du fil se termine. Cela se produira parce que votre thread a remarqué que Terminated es True et démissionne. Ensuite, le destructeur du fil peut continuer et terminer le travail de rangement du fil.


En regardant le code dans votre réponse, il serait mieux écrit de faire usage de l'existant Terminate mécanisme.

type
  TMyThread = class(TThread)
  private
    FTerminateEvent: TEvent;
  protected
    procedure Execute; override;
    procedure TerminatedSet; override;
  public
    constructor Create(ACreateSuspended: Boolean);
    destructor Destroy; override;
  end;

constructor TMyThread.Create(ACreateSuspended: Boolean);
begin
  inherited Create(ACreateSuspended);
  FTerminateEvent := TEvent.Create(nil, True, False, '');
end;

destructor TMyThread.Destroy;
begin
  inherited;
  FTerminateEvent.Free;
end;

procedure TMyThread.TerminatedSet;
begin
  FTerminateEvent.SetEvent;
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    // do somthing interesting!
    FTerminateEvent.WaitFor(5000);
  end;
end;

Maintenant, il n'y a pas besoin d'une Stop méthode. Vous pouvez simplement appeler Free sur le fil. Puis Terminate est appelé. Et puis TerminatedSet est appelé. Puis l'événement est signalé. Puis Execute se termine. Et puis le fil peut disparaître.


Cela dit, j'ai du mal à imaginer un scénario dans lequel un délai de 5000 ms serait la meilleure approche. Je ne sais pas pourquoi vous faites cela, mais je suppose que vous essayez d'étrangler le thread pour qu'il ne s'exécute pas chaud . Vous voulez éviter une boucle occupée. C'est admirable, mais l'utilisation d'un délai d'attente fixe n'est pas la bonne façon de procéder. La méthode consiste à attendre un événement de synchronisation, généralement un événement. Ensuite, lorsqu'il y a plus de travail à faire, l'événement est signalé et votre thread se réveille.

2voto

S.MAHDI Points 992

L'utilisation de TEvent peut résoudre ce problème Voici un exemple :

  uses SyncObjs;
  TMyThread = class(TThread)
       private
         FTerminateEvent: TEvent;
       protected
         procedure Execute; override  ;
       public
        constructor Create(ACreateSuspended: Boolean); overload;
        destructor Destroy; override;
        procedure Stop;
       end;
    constructor TMyThread.Create(ACreateSuspended: Boolean);
    begin
    FTerminateEvent := TEvent.Create(nil, True, False, 'FTerminateEvent');
    inherited Create(ACreateSuspended);
    end;

    destructor TMyThread.Destroy;
    begin
     FTerminateEvent.Free;
     inherited;
    end;

    procedure TMyThread.Stop;
    begin
    Terminate;
    FTerminateEvent.SetEvent;
    end;

    procedure TMyThread.Execute;
    begin
      while not Terminated do
      begin
      // do somthing interesting!
       FTerminateEvent.WaitFor(5000);
      end;
    end;

Maintenant, si je veux tuer mon thread, tout ce que j'ai à faire est d'appeler MyThread.Stop puis d'appeler MyThread.Free .

1voto

David Schwartz Points 70129

Vous abordez vraiment ce problème de manière complètement erronée. Codez vos fils pour qu'ils ne fassent que le travail que vous voulez qu'ils fassent, et alors il n'y aura pas besoin de "toucher de l'extérieur" pour les contrôler. Vous ressentez le besoin de contrôler ce fil de l'extérieur parce qu'il fait quelque chose que vous ne voulez pas qu'il fasse - alors pourquoi l'avez-vous codé pour qu'il fasse cela en premier lieu ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X