2012-05-01 19 views
7

Ho un'applicazione Delphi che genera 6 thread anonimi su alcuni eventi TTimer.OnTimer.Come terminare i thread anonimi in Delphi alla chiusura dell'applicazione?

Se chiudo l'applicazione dal pulsante X nella barra del titolo Violazione di accesso all'indirizzo $ C0000005 viene generato e i report FastMM hanno perso oggetti TAnonymousThread.

Qual è il modo migliore per liberare thread anonimi in Delphi creati all'interno dell'evento OnTimer con il metodo TThread.CreateAnonymousThread()?

soluzione che ha funzionato per me:

Creato un wrapper dei fili anonimi che li terminano sull'essere Free-ed.

type 
    TAnonumousThreadPool = class sealed(TObject) 
    strict private 
    FThreadList: TThreadList; 
    procedure TerminateRunningThreads; 
    procedure AnonumousThreadTerminate(Sender: TObject); 
    public 
    destructor Destroy; override; final; 
    procedure Start(const Procs: array of TProc); 
    end; 

{ TAnonumousThreadPool } 

procedure TAnonumousThreadPool.Start(const Procs: array of TProc); 
var 
    T: TThread; 
    n: Integer; 
begin 
    TerminateRunningThreads; 

    FThreadList := TThreadList.Create; 
    FThreadList.Duplicates := TDuplicates.dupError; 

    for n := Low(Procs) to High(Procs) do 
    begin 
    T := TThread.CreateAnonymousThread(Procs[n]); 
    TThread.NameThreadForDebugging(AnsiString('Test thread N:' + IntToStr(n) + ' TID:'), T.ThreadID); 
    T.OnTerminate := AnonumousThreadTerminate; 
    T.FreeOnTerminate := true; 
    FThreadList.LockList; 
    try 
     FThreadList.Add(T); 
    finally 
     FThreadList.UnlockList; 
    end; 
    T.Start; 
    end; 
end; 

procedure TAnonumousThreadPool.AnonumousThreadTerminate(Sender: TObject); 
begin 
    FThreadList.LockList; 
    try 
    FThreadList.Remove((Sender as TThread)); 
    finally 
    FThreadList.UnlockList; 
    end; 
end; 

procedure TAnonumousThreadPool.TerminateRunningThreads; 
var 
    L: TList; 
    T: TThread; 
begin 
    if not Assigned(FThreadList) then 
    Exit; 
    L := FThreadList.LockList; 
    try 
    while L.Count > 0 do 
    begin 
     T := TThread(L[0]); 
     T.OnTerminate := nil; 
     L.Remove(L[0]); 
     T.FreeOnTerminate := False; 
     T.Terminate; 
     T.Free; 
    end; 
    finally 
    FThreadList.UnlockList; 
    end; 
    FThreadList.Free; 
end; 

destructor TAnonumousThreadPool.Destroy; 
begin 
    TerminateRunningThreads; 
    inherited; 
end; 

Fine ecco come si può chiamare:

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    FAnonymousThreadPool.Start([ // array of procedures to execute 
    procedure{anonymous1}() 
    var 
     Http: THttpClient; 
    begin 
     Http := THttpClient.Create; 
     try 
     Http.CancelledCallback := function: Boolean 
      begin 
      Result := TThread.CurrentThread.CheckTerminated; 
      end; 
     Http.GetFile('http://mtgstudio.com/Screenshots/shot1.png', 'c:\1.jpg'); 
     finally 
     Http.Free; 
     end; 
    end, 

    procedure{anonymous2}() 
    var 
     Http: THttpClient; 
    begin 
     Http := THttpClient.Create; 
     try 
     Http.CancelledCallback := function: Boolean 
      begin 
      Result := TThread.CurrentThread.CheckTerminated; 
      end; 
     Http.GetFile('http://mtgstudio.com/Screenshots/shot2.png', 'c:\2.jpg'); 
     finally 
     Http.Free; 
     end; 
    end 
    ]); 
end; 

Nessuna perdita di memoria, una corretta chiusura e facile da usare.

+1

"thread anonimi" - Oh grandioso .. cosa ci ha imposto Embarcadero ora? –

+1

@ Martin: Niente di spaventoso, davvero. È un thread il cui comportamento viene fornito al momento della creazione da un metodo anonimo. Ti consente di utilizzare chiusure durante la definizione dei thread. –

+0

È continuamente creare/distruggere discussioni, qualcosa che ho passato negli ultimi 20 anni a dire agli sviluppatori di evitare. Tuttavia, se non è effettivamente iniziato, non riesco a capire perché dovrebbe esserci un AV. –

risposta

14

Se si desidera mantenere ed esercitare il controllo sulla durata di un thread, è necessario impostare FreeOnTerminate su False. Altrimenti è un errore riferirsi alla discussione dopo che ha iniziato l'esecuzione. Questo perché una volta avviato, non hai modo di sapere se è stato liberato o meno.

La chiamata a CreateAnonymousThread crea una discussione con FreeOnTerminate impostata su True.

The thread is also marked as FreeOnTerminate, so you should not touch the returned instance after calling Start.

E così, ma di default, non si è in grado di esercitare il controllo sulla durata del thread. Tuttavia, è possibile impostare FreeOnTerminate a False immediatamente prima di chiamare Start. Mi piace:

MyThread := TThread.CreateAnonymousThread(MyProc); 
MyThread.FreeOnTerminate := False; 
MyThread.Start; 

Tuttavia, non sono sicuro che lo farei. Il design di CreateAnonymousThread è che il thread viene automaticamente liberato al termine. Penso che personalmente seguirò il design previsto o deriverò il mio discendente TThread.

+0

Vedo. Ma ancora non capisco. TAnonymousThread è un discendente di TThread. Quindi posso mantenere un elenco di essi e provare a terminarli (poiché RTL non lo fa automaticamente). Quando provo a terminarlo, dice: Progetto mtgstudio.exe ha sollevato la classe di eccezioni EThread con il messaggio "Errore thread: l'handle non è valido (6)". –

+0

Leggi i documenti per il thread anonimo. Non ti è permesso tenere un riferimento al TThread poiché utilizza FreeOnTerminate. –

+0

Siamo spiacenti, non posso guardare questo progetto. Penso di aver risposto alla domanda che hai chiesto. In sostanza, si avvia un thread anonimo perderne il controllo. Non puoi più fare riferimento a questo. –

3

Fai in modo che le tue discussioni attengano a qualche tipo di notifica dall'esterno. Questo potrebbe essere un evento che viene segnalato, un messaggio inviato a una finestra di proprietà del thread, un comando inviato su un socket che il thread ascolta o qualsiasi altra forma di comunicazione trovata.

Se si determina che questo problema è dovuto al fatto che i thread sono i cosiddetti thread "anonimi", una soluzione semplice consiste nel farli diventare thread non anonimi. Metti il ​​corpo della funzione anonima nel metodo Execute e passa qualsiasi variabile catturata alla classe thread tramite il suo costruttore.

+0

Puoi facilmente passare un metodo anonimo a un discendente TThread ed eseguirlo nel metodo Execute. –

+0

Ovviamente, @David. Non è la prova di 'CreateAnonymousThread'? E allora? –

+1

Intendo dire che sarebbe un'alternativa all'approccio suggerito nella frase finale della tua risposta. –

8

Per evitare errori utilizzando CreateAnonymousThread, è sufficiente impostare FreeOnTerminate su False prima di avviarlo.

In questo modo è possibile lavorare con il thread come di consueto senza alcuna soluzione.

È possibile leggere la documentazione che dice che CreateAnonymousThread imposta automaticamente FreeOnTerminate-True e questo è ciò che sta causando gli errori quando si fa riferimento al thread.

Problemi correlati