Jednym z powodów używania wątków jest możliwość przerwania wykonywanych przezeń czynności właściwie w dowolnym momencie. Dzięki temu można na przykład wyposażyć aplikację okienkową w magiczny przycisk Anuluj
obok paska postępu. Przez to zaś użytkownik ma wrażenie, że – nawet jeśli musi (dłuższą) chwilę zaczekać – nadal jest panem sytuacji :)
Jak można więc przerwać wątek, jeśli zachodzi taka potrzeba? Sposobów jest kilka:
while (!Terminating) { /* ... */ }
. Dla porządku warto też (a w niektórych środowiskach trzeba), po ustawieniu rzeczonej flagi na wątek zaczekać. Czyni się to zwykle funkcją z join (‘złącz’) w nazwie: Thread.Join/join
w .NET/Javie, pthread_join
w POSIX-ie i… WaitForSingleObject
w Windows API :)Thread.Interrupt/interrupt
. Wówczas przy następnym wejściu do funkcji czekającej w rodzaju Thread.Sleep/sleep
rzucany jest wyjątek (Thread)InterruptedException
, który odwija stos wątku, kończąc w ten sposób jego działanie. W natywnych platformach nie może być oczywiście dokładnego odpowiednika podobnej operacji, lecz można ją symulować czekaniem na jakimś obiekcie synchronizacyjnym i sygnalizowaniem go, gdy chcemy to czekanie przerwać. W POSIX-ie funkcja pthread_cancel
działa aczkolwiek w dość podobny sposób do Interrupt
, pozwalając na posprzątanie zasobów w trakcie anulowania wątku.Thread.Abort/stop
), w wątku rzucany jest bardzo specjalny wyjątek (ThreadAbortException
lub ThreadDeath
). Ma on tę cechę, że… przechodzi przez (prawie) wszystkie bloki catch
– w .NET właściwie nie można go “zdusić”. To jednak sprawia, że taki wyjątek może wykonać po drodze wszystkie bloki finally
, co pozwala zwolnić zawłaszczone zasoby i blokady oraz przywrócić obiekty synchronizujące do właściwego stanu. Występująca w Windows API funkcja TerminateThread
takiej możliwości już nam nie daje, przez co jej użycie może prowadzić do różnych kłopotów.Wnioski z tych trzech sposobów są mniej więcej takie, że pisząc kod wykonywany w osobnym wątku powinniśmy zawsze przewidzieć “czysty” sposób na jego zakończenie. Poleganie na przerywaniu lub brutalnym zakańczaniu wątków może się bowiem źle skończyć.
Ad2. Czyli w natywnych aplikacjach nigdy nie czekamy na SingleObject tylko na MultipleObject, gdzie dodatkowym warunkiem jest flaga oznaczająca anulowanie pracy wątku.
To jest wszystko proste kiedy wątek coś liczy. Ale powstaje problem, co zrobić z wywołaniami funkcji wejścia-wyjścia (dyskowe, sieciowe). Jeśli one blokują czekając na dane, to jak przerwać taki wątek w dowolnym momencie? Dlatego wielowątkowość wbrew pozorom niewiele daje pod tym względem. Należy raczej stosować nieblokujące (aynchroniczne) funkcje wejścia-wyjścia.