Pytanie w sprawie delphi, tthread, destructor – Właściwy sposób zniszczenia obiektu TThread

15

To pytanie może wydawać się banalne, ale mam nadzieję, że tego nie zignorujesz.
Przed zniszczeniem obiektu TThread zazwyczaj trzeba poczekać, aż zakończy się wątek zwany metodą TThread.Execute (), ponieważ tylko wtedy możemy być pewni, że na przykład obiekty zniszczone wewnątrz destruktora klasy nie będą już dostępne. Dlatego konieczne jest wywołanie Terminate, aby ustawić flagę Zakończone, aby wątek musiał sprawdzić, czy ma wyjść, czy nie, a następnie wywołać metodę WaitFor ().

Ponieważ wątek może zostać zawieszony, myślę, że dobrze jest wznowić go przed wywołaniem WaitFor, ponieważ w przeciwnym razie wątek wywołujący byłby zablokowany. A ponieważ wątek może być zawieszony wiele razy, należy go wznowić tyle samo razy, prawda?

while Suspended do
  Resume;

Jeśli wątek został utworzony w zawieszeniu, nie musimy się martwić, że metoda TThread.Execute () zostanie wywołana, gdy wznowimy wątek tylko po to, aby go zakończyć - nie będzie (proszę poprawić mnie, jeśli się mylę).

To, co stwierdziłem, sugeruje użycie następujących wierszy kodu dla każdego zwolnionego obiektu TThread:

MyThread.Terminate;
while MyThread.Suspended do
  MyThread.Resume;
MyThread.WaitFor;
MyThread.Free;

Niestety, kiedy zniszczymy naszą aplikację, która stworzyła wiele wątków, napisanie takiego fragmentu kodu dla każdego niszczonego obiektu TThread niepotrzebnie czyni kod bardzo długim, a może nawet nieprzejrzystym.

Dlatego doszedłem do wniosku, że wszystko to można umieścić w nadpisanym destruktorze klasy TThread, dzięki czemu wystarczy wywołać MyThread.Free (lub MyThread.Terminate, jeśli ustawiona jest MyThread.FreeOnTerminate) bez dbania o to, czy zniszczony obiekt jest obiektem TThread, czy nie:

destructor TMyThread.Destroy;
begin
  //if FreeOnTerminate, the calling thread cannot wait for itself
  if GetCurrentThreadId <> ThreadId then
  begin
    Terminate;
    while Suspended do
      Resume;
    WaitFor;
  end;

  {free all objects created in this class}

  inherited Destroy;
end;

Wybacz mi zadając takie podstawowe pytanie. Chciałbym jednak poznać wasze opinie na temat tego sposobu - mam nadzieję na uniwersalny sposób - niszczenia obiektów TThread. Zadaję te pytania, ponieważ dowiedziałem się z kodów moich kolegów z pracy, że zwykle używali pierwszego przykładu kodu do niszczenia takich obiektów, ale nigdy nie sprawdzali, czy oczekujące wątki nie zostały zawieszone, co uważałem za nieco niebezpieczne, jeśli wątki może zostać zawieszone gdzieś w kodzie. Dlatego starałem się znaleźć uniwersalny sposób niszczenia obiektów tej klasy, który uczyniłby kod jaśniejszym i bezpieczniejszym. Mam nadzieję, że nie pogorszyłem - co o tym myślisz?

Dziękujemy za sugestie z góry.

Przypomnienie ze słów Pana Poloniusza Hamleta: Zwięzłość jest duszą dowcipu Argalatyr

Twoja odpowiedź

2   odpowiedź
8

tak jak nie ma uniwersalnego sposobu (wdzięcznego) zatrzymania procesu. Każdy jest inny.

W przypadku niektórych wątków wystarczy ustawićTerminated nieruchomość przezTerminate metoda. Jednak inne wątki wywołują funkcje podobneGetMessage lubMsgWaitForMultipleObjects, który zablokuje się, dopóki coś się nie wydarzy, na przykład wiadomość przychodząca lub sygnalizacja uchwytu jądra.TThread.Terminate nie może sprawić, że któraś z tych rzeczy się wydarzy, więc nie może sprawić, że te wątki przestaną działać. Kiedy pisałem takie wątki, udostępniłem własne funkcje, aby powiadomić ich, aby przestali działać. Mogę zadzwonićPostThreadMessage aby wymusić wysłanie wiadomości do kolejki wątku, lub mogę zasygnalizować zdarzenie, które klasa wątku udostępniła, aby powiadomić o żądaniu zakończenia.

Nie martw się o wznowienie zawieszonej nici. Tak naprawdę nie powinieneś ich zawieszać. Jedynym bezpiecznym sposobem na zawieszenie wątku jest zawieszenie się wątku, a gdy już to zrobisz, masz pewność, że co najmniej dwa wątki kontrolują, kiedy wątek działa: sam wątek, aby go zawiesić, i co najmniej jeden inny wątek, aby wznowić go ponownie. Wątek powinien kontrolować własne wykonanie.

Byłoby wspaniale, gdybyTThread.Terminate były wirtualne. Następnie każda klasa wątków może dostarczyć niestandardowy sposób powiadamiania się, że powinna przestać działać. Niektórzy mogli po prostu ustawićTerminatedi inni mogą wysyłać wiadomości, sygnalizować zdarzenia lub robić wszystko, czego potrzebują. Jednak nie-wirtualna metoda nie działa dobrze w przypadku wątków, które spędzają dużo czasu czekając na inne rzeczy. Obecny sposób działa tylko w przypadku wątków, które są w stanie częstogłosowanie ichTerminated nieruchomości.

Niektóre wątki mają swojeFreeOnTerminate zestaw właściwości. Dla tych wątków twój kod nie jest bezpieczny. Technicznie nie można dzwonićkażdy metody na takich obiektach, ponieważ wątek może się zakończyć w dowolnym momencie. Ale nawet jeśli wiesz, że wątek nadal działa, a obiekt wątku nadal istnieje, obiekt na pewno przestanie istnieć po wywołaniuTerminate. Nie możesz zadzwonićWaitFor na obiekcie wątku wolnego od końca, a na pewno nie możesz wywołaćFree.

8

estroy i wywołanie TMyThread.free zrobi dokładnie to, co zasugerujesz. Aby oczyścić wszystkie obiekty należące do klasy wątków, można wykonać to w zdarzeniu OnTerminate, które zostanie wywołane jako część logiki zamykania wątku.

Tak, to prawda, jednak sprawdziłem, że tylko wtedy, gdy wątek jest tworzony, jest wznawiany, gdy wywoływany jest destruktor. Kiedy zawiesię go podczas pracy, wywołanie destruktora bez wznowienia spowoduje zakleszczenie. I dlatego szukałem, powiedzmy, uniwersalnego sposobu niszczenia obiektów TThread, który byłby w porządku dla każdej klasy dziedziczącej po TThread. Mariusz Schimke
@Mariusz: Spróbuj kodować bez Suspend () i Resume (), a nie będziesz miał tego problemu. Metody te mogą być bardzo problematyczne, jak sam się znalazłeś, i zawsze istnieje sposób na ich kodowanie bez nich. Niech zamiast tego wątek zablokuje jeden lub kilka dostarczonych przez system prymitywów synchronizacji. Lub użyj pętli wiadomości, istnieje kilka odpowiedzi z informacjami na temat wielowątkowości tutaj na SO w [delphi]. mghie

Powiązane pytania