Zakończenie wątku w C # i Thread.Abort ()


85

W witrynie MSDN opis metody Thread.Abort () mówi: „Wywołanie tej metody zwykle kończy wątek”.

Dlaczego nie ZAWSZE?

W jakich przypadkach nie kończy wątku?

Czy są jakieś inne możliwości zakończenia wątków?

Odpowiedzi:


60

Thread.Abort()wstrzykuje a ThreadAbortExceptionna nitkę. Wątek może anulować żądanie, dzwoniąc Thread.ResetAbort(). Istnieją również pewne części kodu, takie jak finallyblok, który zostanie wykonany przed obsłużeniem wyjątku. Jeśli z jakiegoś powodu wątek utknie w takim bloku, wyjątek nigdy nie zostanie zgłoszony w wątku.

Ponieważ wywołujący ma bardzo małą kontrolę nad stanem wątku podczas wywoływania Abort(), generalnie nie jest to zalecane. Zamiast tego przekaż wiadomość do wątku żądającego zakończenia.


Jaka wiadomość? Czy masz na myśli wywołanie metody?
user101375

4
To zależy od tego, jak przekazujesz dane między wątkami. Np. Jeśli twój wątek jest wątkiem roboczym z kolejką zadań, możesz ustawić w kolejce komunikat PleaseTerminate i pozwolić wątkowi na obsługę go wdzięcznie.
Brian Rasmussen

Mój wątek ma jeden duży problem, w którym masz do rozwiązania, nie ma kolejki zadań.
user101375

Jak powiedziałem, zależy to od sposobu zaprojektowania kodu wątku. Być może mógłbyś wtedy zasygnalizować za pomocą zmiennej składowej. Trudno jest być bardziej szczegółowym bez faktycznego dostępnego kodu.
Brian Rasmussen

54

W jakich przypadkach nie kończy wątku?

To pytanie jest powtórzone.

Co jest złego w używaniu Thread.Abort ()

Czy są jakieś inne możliwości zakończenia wątków?

Tak. Twoim problemem jest to, że nigdy nie powinieneś rozpoczynać wątku, którego nie możesz grzecznie przerwać, i kończy się on w odpowiednim czasie. Jeśli jesteś w sytuacji, w której musisz uruchomić wątek, który może być (1) trudny do zatrzymania, (2) błędny lub najgorszy (3) wrogi dla użytkownika, to właściwą rzeczą jest nowy proces, uruchom wątek w nowym procesie, a następnie zakończ proces, gdy chcesz, aby wątek został wyłączony. Jedyną rzeczą, która może zagwarantować bezpieczne zakończenie niewspółpracującego wątku, jest wstrzymanie całego procesu przez system operacyjny.

Zobacz moją zbyt długą odpowiedź na to pytanie, aby uzyskać więcej informacji:

Używanie instrukcji lock w pętli w C #

Istotnym fragmentem jest fragment na końcu, w którym omawiam rozważania dotyczące tego, jak długo należy czekać, aż wątek sam się zabije, zanim go przerwiesz.


Eric, jak mam grzecznie powiedzieć wątkowi, aby się zatrzymał, jeśli ten wątek oczekuje na obiekt synchronizacji, np. EventWaitHandle.WaitOne () ;?
Corvin

5
@Corvin: kontrakt między dwoma wątkami, który opisuje, w jaki sposób jeden wątek komunikuje się z innym, zależy od autorów kodu, który działa w tych wątkach. Jeśli masz wymagania, które (1) wątek X musi czekać na obiekt potencjalnie w nieskończoność i (2) ten wątek Y musi być w stanie czysto zamknąć wątek X w skończonym czasie, to myślę, że masz sprzeczne wymagania; zdecyduj, który wygrywa. Jeśli to pierwsze, wątek Y będzie musiał czekać. Jeśli to drugie, wątek X nie powinien czekać wiecznie, powinien odczekać trochę czasu.
Eric Lippert

Dzięki za odpowiedź. Rozważ następującą architekturę: Mam aplikację, która musi się zminimalizować, gdy wystąpi określone zdarzenie dotyczące całego systemu. Mam wątek w tej aplikacji, który czeka na globalne nazwane zdarzenie i wykonuje swoją pracę, gdy to zdarzenie jest ustawione. Z drugiej strony może być konieczne zamknięcie mojej aplikacji przed wystąpieniem zdarzenia, co spowoduje zamknięcie tego oczekującego wątku. Jaki jest samurajski sposób na zakodowanie czegoś takiego?
Corvin

@Corvin: Możesz mieć pętlę, która czeka (z krótkim limitem czasu) na zdarzenie, a następnie sprawdza, czy powinna przestać próbować czekać (aby mogła normalnie wyjść). W ten sposób możesz zasygnalizować swojemu wątkowi, że powinien się zatrzymać, i powinieneś czekać tylko tak długo, jak upłynie limit czasu, aby się zatrzymał.
Kevin Kibler

2
@Corvin, jeśli ten wątek oczekujący stanie się wątkiem w tle, nie powinieneś go zamykać przed zamknięciem aplikacji.
Don Kirkby,

17

Dlaczego nie ZAWSZE? W jakich przypadkach nie kończy wątku?

Na początek wątek może przechwycić ThreadAbortExceptioni anulować własne zakończenie. Lub może wykonać obliczenia, które trwają wiecznie, gdy próbujesz je przerwać. Z tego powodu środowisko wykonawcze nie może zagwarantować, że wątek zawsze zakończy się, gdy go o to poprosisz.

ThreadAbortException ma wiecej:

Gdy wywoływana jest metoda Abort w celu zniszczenia wątku, środowisko uruchomieniowe języka wspólnego zgłasza ThreadAbortException. ThreadAbortException to specjalny wyjątek, który można przechwycić, ale zostanie on automatycznie zgłoszony ponownie na końcu bloku catch. Gdy ten wyjątek zostanie zgłoszony, środowisko uruchomieniowe wykonuje wszystkie ostatnie bloki przed zakończeniem wątku. Ponieważ wątek może wykonać nieograniczone obliczenia w Thread.ResetAbort()końcowych blokach lub wywołać anulowanie przerwania, nie ma gwarancji, że wątek kiedykolwiek się zakończy.

Nie musisz Abort()ręcznie zakładać wątku. CLR wykona całą brudną robotę za Ciebie, jeśli po prostu pozwolisz metodzie w wątku powrócić; to zakończy wątek normalnie.


Muszę to przerwać (), ponieważ w tym wątku rozpoczęła się pętla komunikatów (Dispatcher.Run ();). Jeśli dobrze rozumiem, muszę wywołać metodę wywołującą powrót do wątku, aby ją zakończyć?
user101375

7

FileStream.Read()do nazwanego potoku, który aktualnie niczego nie odbiera (odczytuje bloki wywołań podczas oczekiwania na przychodzące dane) nie odpowiada Thread.Abort(). Pozostaje wewnątrz Read()rozmowy.


6

Co się stanie, jeśli wątek zawiera blokadę i zostanie przerwany / zabity? Zasoby pozostają zablokowane

Działa dobrze, gdy wątek wywołuje przerwanie, ale nie inny wątek. Przerwij, wymuszenie kończy wątek, którego dotyczy problem, nawet jeśli nie ukończył zadania i nie daje możliwości wyczyszczenia zasobów

odniesienie MSDN


zobacz: Najlepsze praktyki w zarządzaniu wątkami


5

Nie mogę przerwać wątku, który utknął w pętli:

//immortal
Thread th1 = new Thread(() => { while (true) {}});

Mogę jednak przerwać wątek, jeśli śpi podczas pętli:

//mortal
Thread th2 = new Thread(() => { while (true) { Thread.Sleep(1000); }});

1
to nie wygląda na prawdę, uruchomiłem program testowy: `{var th = new Thread (() => {int i = 0; try {while (true) {i ++;}} catch (Exception e) { Console.WriteLine ("aborting @ {0}", i);}}); th.Start (); Thread.Sleep (1000); th.Abort (); th.Join (); }
Weipeng L


3

Ponieważ możesz złapać ThreadAbortExceptioni wywołać Thread.ResetAbortwewnątrz handlera.


0

OT: Aby zapoznać się z obszernym, niezależnym od języka, wątpliwie użytecznym i cholernie zabawnym podejściem do współbieżności, zobacz Verity Stob !


-1

Miałem przypadki, w których wątek był zbyt zajęty, aby usłyszeć wywołanie Abort (), co zwykle powoduje zgłoszenie wyjątku ThreadAbortingException do mojego kodu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.