2013-08-01 11 views
5

Tutto ciò che ho letto afferma che un abort su un thread eseguirà il blocco finally prima di terminare da un ThreadAbortException. Volevo confermarlo, così posso pianificare come gestire un codice di terze parti che può essere sospeso indefinitamente. Tuttavia il seguente test mi ha confuso:È possibile ignorare ThreadAbortException?

public void runTest(DateTime deadline) 
{ 
    testThread = new Thread(() => 
    { 
     try 
     { 
      Console.WriteLine("test thread started at " + DateTime.Now.ToShortTimeString()); 
      while (true) { } 
     } 
     finally 
     { 
      Console.WriteLine("test thread entered FINALLY at " + DateTime.Now.ToShortTimeString()); 
      while (true) { } 
     } 
    }); 
    testThread.Start(); 
    while (testThread.IsAlive && deadline.Subtract(DateTime.Now).TotalSeconds > 0) 
    { 
     Console.WriteLine("main thread while loop " + DateTime.Now.ToShortTimeString()); 
     Thread.Sleep(10000); 
    } 
    if (testThread.IsAlive) 
     testThread.Abort(); 
    Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString()); 
} 

Quello che ho trovato durante l'esecuzione di questo è che console mai cita entrare nel blocco finally. L'applicazione continua dopo la chiamata .abort come se non ci fosse alcun blocco finale. Sto facendo qualcosa di sbagliato? Non dovresti controllare il passaggio al blocco finally prima di raggiungere la scrittura finale in console o l'ordine di esecuzione è ancora una funzione del fatto che alla fine si trova in una discussione separata o qualcosa del genere?

+0

Ho eseguito il codice e ho ottenuto 'thread di test inserito FINALMENTE alle 3:21 PM'. – Romoku

+0

Non provato, ma ho il sospetto che il codice si comporterà in modo diverso tra debug e release/no debugger configurate come loop infinito che potrebbe essere JITed in qualcosa che non può essere interrotto in release build. –

+2

Chiamare 'Thread.Abort' è come fermare una macchina sparando al guidatore in testa. L'auto si fermerà, ma non si sa cosa potrebbe succedere nel frattempo. Non dovresti * assolutamente * aver bisogno di chiamare 'Thread.Abort'. Se lo fai, allora hai bisogno di rivisitare il design della tua applicazione. –

risposta

6

Docs say: ThreadAbortException è un'eccezione speciale che può essere rilevata, ma verrà automaticamente aumentata di nuovo alla fine del blocco catch. Quando viene sollevata questa eccezione, il runtime esegue tutti i blocchi finally prima di terminare il thread. Poiché il thread può eseguire un calcolo illimitato negli ultimi blocchi o chiamare Thread.ResetAbort per annullare l'interruzione, non vi è alcuna garanzia che il thread finirà mai.

Sono quasi sicuro che il thread è stato scaricato perché si esce dal metodo e si perde un riferimento ad esso e quindi viene raccolto dal Garbage Collector. Prova a rendere la variabile testThread un membro del campo della classe e guarda cosa succede.

Questo, o si ha una condizione di competizione poiché i thread sono eseguiti in parallelo: il thread principale sta terminando prima che il thread di test spun-up possa emettere i dati finali (le eccezioni sono costose e richiedono tempo per raggiungere il catch o infine i blocchi) .

+3

I thread in esecuzione generalmente non vengono raccolti. Vedi: http://stackoverflow.com/questions/81730/what-prevents-a-thread-in-c-sharp-from-being-collected –

+0

La mia esperienza è stata diversa, tuttavia sembra che l'uscita sia forse una condizione di competizione tra il thread principale e il thread generato? Aggiornato la mia risposta, grazie per l'input @ebyrob – Haney

+0

L'ho provato di nuovo alcune volte dopo che Romoku ha menzionato che funzionava per lui. Ciò conferma il mio sospetto che il thread non venga necessariamente annullato prima che il controllo ritorni al thread principale. Ho aggiunto un file .join dopo l'interruzione e ora rimane in definitiva senza mai chiamare la console finale per scrivere nel thread principale. Quindi DavidH ha ragione, il loro è un tipo di condizione di gara in cui stanno succedendo qui. – user1807768

2

Non è consigliabile saltare il numero finally.

È possibile che l'app della console (supponendo che sia uno) stia uscendo prima delle esecuzioni del blocco finally (poiché non c'è attesa dopo aver chiamato Thread.Abort()).

Cosa succede se si inserisce un valore Console.ReadLine() alla fine del programma?

5

Il blocco finally nella funzione thread di lavoro viene eseguito sul thread di lavoro che è parallelo alla thread principale. È una condizione di gara. Non è possibile stabilire quale di fine thread del codice worker o main thread dopo l'interruzione della chiamata venga eseguita prima. Se avete bisogno di un interruzione sincrona poi si deve mettere qualcosa di simile:

 if (testThread.IsAlive) 
     { 
      testThread.Abort(); 

      bool blnFinishedAfterAbort = testThread.Join(TimeSpan.FromMilliseconds(1000)); 
      if (!blnFinishedAfterAbort) 
      { 
       Console.WriteLine("Thread abort failed."); 
      } 
     } 
     Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString()); 

Tenete presente che se avete la gestione delle eccezioni legacy abilitati (vedi http://msdn.microsoft.com/en-us/library/ms228965.aspx) e si specifica il AppDomain_UnahandledException gestore evento, allora il ThreadAbortException sarà condurre l'esecuzione a quell'handler PRIMA del blocco finally nella funzione thread di lavoro. Questo è solo un altro esempio di ordine di esecuzione frustrante e inaspettato di cui dovresti essere a conoscenza mentre interrompi i thread.

Problemi correlati