2013-08-01 31 views
5

Wszystko, co przeczytałem, twierdzi, że przerwano wątek, spowoduje wykonanie bloku finally przed zakończeniem wyjątku ThreadAbortException. Chciałem to potwierdzić, abym mógł zaplanować obsługę kodu innej firmy, który może zawieszać się w nieskończoność. Jednak następujący test mnie mylić:Czy ThreadAbortException może w końcu przejść?

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()); 
} 

Co znajdę, gdy uruchomiony jest to, że konsola nie wspomina wprowadzając wreszcie zablokować. Aplikacja będzie kontynuowana po wywołaniu .abort, tak jakby w ogóle nie było żadnego bloku końcowego. czy robię coś źle? Czy nie powinno się kontrolować przechodzenia do bloku końcowego przed osiągnięciem ostatecznego zapisu na konsolę, czy kolejność wykonania nadal jest funkcją faktu, że ostatecznie znajduje się w osobnym wątku czy coś takiego?

+0

Uruchomiłem kod i otrzymałem "wątek testowy wprowadzony WRESZCIE o 15:21". – Romoku

+0

Nie wypróbowany, ale podejrzewam, że kod będzie zachowywał się inaczej między debugowaniem i wersją/brakiem dołączonych konfiguracji debuggera jako nieskończonej pętli, która może być JITed w coś, co nie może zostać przerwane w kompilacji wydania. –

+2

Wywołanie "Thread.Abort' jest jak zatrzymanie samochodu przez wystrzelenie sterownika w głowę. Samochód się zatrzyma, ale nie wiadomo, co może się stać w międzyczasie. Naprawdę nie powinieneś * nigdy * potrzebować połączenia z "Thread.Abort". Jeśli to zrobisz, musisz ponownie przejrzeć projekt aplikacji. –

Odpowiedz

6

Docs say: ThreadAbortException to specjalny wyjątek, który może zostać przechwycony, ale zostanie automatycznie podniesiony ponownie na końcu bloku catch. Gdy ten wyjątek zostanie podniesiony, środowisko wykonawcze wykonuje wszystkie bloki finally przed zakończeniem wątku. Ponieważ wątek może wykonać nieograniczone obliczenia w blokach finally lub wywołać Thread.ResetAbort, aby anulować przerwanie, nie ma gwarancji, że wątek kiedykolwiek się skończy.

Jestem prawie pewny, że wątek jest odrzucany, ponieważ wychodzisz z metody i tracisz odniesienie do niej, a więc zostaje ona zebrana przez Garbage Collector. Spróbuj ustawić zmienną testThread na element pola klasy i zobacz, co się stanie.

Tak, lub masz stan wyścigu, ponieważ wątki przebiegają równolegle: główny wątek kończy się, zanim wątek testowy może wyprowadzić dane końcowe (Wyjątki są drogie i wymagają czasu, aby dotrzeć do bloków catch lub finally) .

+3

Uruchamianie wątków zwykle nie jest zbierane. Zobacz: http://stackoverflow.com/questions/81730/what-prevents-a-thread-in-c-sharp-from-being-collected –

+0

Moje doświadczenie różni się, ale wydaje się, że wyjście jest być może stanem wyścigowym pomiędzy główny wątek i spawnowany wątek? Zaktualizowałem moją odpowiedź, dzięki za wejście @ebyrob – Haney

+0

Próbowałem go ponownie kilka razy po tym, jak Romoku wspomniał, że zadziałało dla niego. Potwierdza to moje podejrzenie, że wątek niekoniecznie zostanie przerwany, zanim kontrola powróci do głównego wątku. Dodałem .join po przerwie, a teraz pozostaje on w końcu, nie wywołując końcowego zapisu konsoli w głównym wątku. DavidH ma rację, ich stan to rodzaj wyścigu, coś w tym rodzaju. – user1807768

2

Standardowo nie należy pomijać obiektu .

Jest możliwe, że aplikacja konsoli (zakładając, że jest to jedna) wystaje przed finally tras blokowych (ponieważ nie ma oczekiwania po wywołaniu Thread.Abort()).

Co stanie się, jeśli na końcu programu umieścisz Console.ReadLine()?

5

Ostatni blok w wątku roboczym jest wykonywany na wątku roboczym równoległym do głównego wątku. To jest wyścigowy stan. Nie można określić, który wątek roboczy ma końcowy lub główny kod wątku po tym, jak abort zostanie wywołany wcześniej. Jeśli potrzebujesz synchronicznego przerwanie następnie trzeba umieścić coś takiego:

 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()); 

Pamiętaj, że jeśli masz obsługa spuścizna wyjątek włączona (patrz http://msdn.microsoft.com/en-us/library/ms228965.aspx) i określono AppDomain_UnahandledException obsługi zdarzeń wówczas ThreadAbortException będzie prowadzić wykonanie do tego handlerka PRZED końcowym blokiem w funkcji wątku roboczego. To kolejny przykład frustrującego i nieoczekiwanego porządku wykonania, o którym należy pamiętać podczas przerywania wątków.