2009-11-09 12 views
9

Moja aplikacja tworzy wątek i cały czas działa w tle. Mogę ręcznie zakończyć wątek, a nie z poziomu funkcji wywołania zwrotnego wątku. W tej chwili używam TerminateThread(), aby zabić ten wątek, ale czasami powoduje to zawieszanie. Wiem, że istnieje sposób korzystania z wydarzeń i WaitForSingleObject(), aby zakończyć wątek z wdziękiem, ale nie mogę znaleźć przykładu na ten temat.Kończenie nici z wdziękiem nie używając TerminateThread()

Proszę, kod jest potrzebny tutaj.

Odpowiedz

18

TerminateThread to zły pomysł, zwłaszcza jeśli twój wątek korzysta z obiektów synchronizacji, takich jak muteksy. Może to prowadzić do niewydanej pamięci i uchwytów oraz do zakleszczeń, więc masz rację, że musisz zrobić coś innego.

Zazwyczaj sposobem zakończenia wątku jest powrót z funkcji definiującej wątek. Główny wątek sygnalizuje wątek roboczy do wyjścia przy użyciu obiektu zdarzenia lub nawet prostej wartości boolowskiej, jeśli jest ona często sprawdzana. Jeśli wątek roboczy czeka z numerem WaitForSingleObject, być może trzeba go zmienić na WaitForMultipleObjects, gdzie jednym z obiektów jest zdarzenie. Główny wątek zadzwoniłby pod numer SetEvent, a wątek roboczy zostałby przebudzony i powrócił.

Naprawdę nie możemy podać żadnego użytecznego kodu, chyba że pokażesz nam, co robisz. W zależności od tego, co robi wątek roboczy i jak główny wątek przekazuje mu informacje, może wyglądać zupełnie inaczej.

Ponadto, w [teraz bardzo stary] MSVC, należy użyć _beginthreadex zamiast CreateThread w celu uniknięcia wycieków pamięci w CRT. Zobacz MSKB #104641.

Aktualizacja:

Jednym z zastosowań wątku roboczego jest jako „Timer”, aby zrobić kilka operacji na regularnych odstępach czasu. Najprostszy:

for (;;) { 
    switch (WaitForSingleObject(kill_event, timeout)) { 
     case WAIT_TIMEOUT: /*do timer action*/ break; 
     default: return 0; /* exit the thread */ 
    } 
} 

Innym zastosowaniem jest zrobienie czegoś na żądanie. Zasadniczo to samo, ale z limitem czasu ustawionym na INFINITE i wykonaniem jakiegoś działania na WAIT_OBJECT_0 zamiast na WAIT_TIMEOUT. W tym przypadku potrzebne są dwa zdarzenia, jeden, aby obudzić wątek się i zrobić jakąś akcję, kolejny, aby obudzić i wyjść:

HANDLE handles[2] = { action_handle, quit_handle }; 
for (;;) { 
    switch (WaitForMultipleObject(handles, 2, FALSE, INFINITE)) { 
     case WAIT_OBJECT_0 + 0: /* do action */ break; 
     default: 
     case WAIT_OBJECT_0 + 1: /* quit */ break; 
    } 
} 

Należy pamiętać, że ważne jest, że pętla zrobić coś rozsądnego jeśli WFSO/WFMO zwraca błąd zamiast jednego z oczekiwanych wyników. W obu powyższych przykładach po prostu traktujemy błąd tak, jakbyśmy byli sygnalizowani, aby zakończyć.

Możesz osiągnąć ten sam wynik w pierwszym przykładzie, zamykając uchwyt zdarzenia z głównego wątku, powodując błąd wątku roboczego od WaitForSingleObject i kończąc, ale nie poleciłbym tego podejścia.

+0

Funkcja wywołania zwrotnego wątku jest dla (;;) to wszystko. nic niezwykłego. tak, powtórzę pytanie, używając WaitForSingleObject() i SetEvent() w jaki sposób mogę zasygnalizować mój wątek, który muszę dokończyć? Jeśli zadzwonię do SetEvet (FinishEvent), to co zwróci WaitForSingleObject()? 0? 1? -1? – Uri

+0

@Uri Po wywołaniu 'SetEvent',' WaitForSingleObject' zwróci 'WAIT_OBJECT_0' (co oznacza 0). Kiedy wspomniałeś o używaniu 'WaitForSingleObject', założyłem, że oznacza to, że już korzystałeś z tego zdarzenia, aby zasygnalizować wątek roboczy, aby coś zrobił (poza wyjściem).Zwróć też uwagę, że w niektórych przypadkach musisz sygnalizować wątek wotker, aby wyjść, a następnie poczekać, aż faktycznie się zatrzyma, zanim przejdziesz dalej. (W tym celu możesz użyć 'WaitForSingleObject' na uchwycie nici). –

+0

+1 Oto odpowiedź, którą dałbym. Czy to w ten sposób wiele razy. –

1

Ponieważ nie wiesz, co robi wątek, nie ma możliwości bezpiecznego zakończenia wątku z zewnątrz.

Dlaczego myślisz, że nie możesz zakończyć tego od wewnątrz?

Możesz utworzyć zdarzenie przed uruchomieniem wątku i przekazać uchwyt tego zdarzenia do wątku. Wywołujesz SetEvent() na tym zdarzeniu z głównego wątku, aby zasygnalizować przerwaniu wątku, a następnie WaitForSingleObject na uchwycie wątku, aby poczekać na zakończenie wątku.Wewnątrz pętli wątków wywołujemy WaitForSingleObject() na zdarzeniu, określając limit czasu na 0 (zero), aby wywołanie natychmiast powróciło, nawet jeśli zdarzenie nie zostało ustawione. Jeśli to wywołanie zwraca WAIT_TIMEOUT, zdarzenie nie jest ustawione, jeśli zwraca WAIT_OBJECT_0, jest ustawione. W tym ostatnim przypadku wracasz z funkcji wątku.

Zakładam, że Twój wątek to nie tylko wypalanie cykli procesora w nieskończonej pętli, ale niektóre czekają, może poprzez wywołanie Sleep(). Jeśli tak, możesz zamiast tego spać w WaitForSingleObject, przekazując mu limit czasu.

+1

wątek nic nie robi, sprawdzając tylko, czy określony program jest zawsze uruchomiony. – Uri

0

Co robisz w wątku tła? Jeśli coś zapętlasz, możesz zakończyć wątek w swoim wnętrzu, udostępniając udostępniony publiczny obiekt statyczny (taki jak Boolean), który ustawisz na true z wątku pierwszoplanowego i że wątek w tle sprawdza i kończy poprawnie, gdy jest ustawiony na true .

0

Jest to przykładowy kod do zarządzania wątkami w trybie łączenia wideł. Używa ona struct Thread jako deskryptora wątku.

Załóżmy wprowadzić pewne abstrakcję struktury danych deskryptor wątku:

#include <Windows.h> 

    struct Thread 
    { 
     volatile BOOL stop; 

     HANDLE event; 
     HANDLE thread; 
    }; 

    typedef DWORD (__stdcall *START_ROUTINE)(struct Thread* self, LPVOID lpThreadParameter); 

    struct BootstrapArg 
    { 
     LPVOID arg; 
     START_ROUTINE body; 
     struct Thread* self; 
    }; 

funkcji do użycia dominującego wątku:

  • StartThread() zainicjować tę strukturę i uruchamia nowy wątek.
  • StopThread() inicjuje zakończenie wątku i czeka, aż wątek zostanie faktycznie zakończony. Oczekuje się

    DWORD __stdcall ThreadBootstrap(LPVOID lpThreadParameter) 
    { 
        struct BootstrapArg ba = *(struct BootstrapArg*)lpThreadParameter; 
    
        free(lpThreadParameter); 
    
        return ba.body(ba.self, ba.arg); 
    } 
    
    VOID StartThread(struct Thread* CONST thread, START_ROUTINE body, LPVOID arg) 
    { 
        thread->event = CreateEvent(NULL, TRUE, FALSE, NULL); 
        thread->stop = FALSE; 
        thread->thread = NULL; 
    
        if ((thread->event != NULL) && (thread->event != INVALID_HANDLE_VALUE)) 
        { 
         struct BootstrapArg* ba = (struct BootstrapArg*)malloc(sizeof(struct BootstrapArg)); 
    
         ba->arg = arg; 
         ba->body = body; 
         ba->self = thread; 
    
         thread->thread = CreateThread(NULL, 0, ThreadBootstrap, ba, 0, NULL); 
         if ((thread->thread == NULL) || (thread->thread == INVALID_HANDLE_VALUE)) 
         { 
          free(ba); 
         } 
        } 
    } 
    
    DWORD StopThread(struct Thread* CONST thread) 
    { 
        DWORD status = ERROR_INVALID_PARAMETER; 
    
        thread->stop = TRUE; 
    
        SetEvent(thread->event); 
        WaitForSingleObject(thread->thread, INFINITE); 
    
        GetExitCodeThread(thread->thread, &status); 
        CloseHandle(thread->event); 
        CloseHandle(thread->thread); 
    
        thread->event = NULL; 
        thread->thread = NULL; 
    
        return status; 
    } 
    

Ten zestaw funkcji, które mają być używane z wątku rozpoczętego przez StartThread():

  • IsThreadStopped() - Sprawdź, czy wniosek wypowiedzenia. Musi być użyty po oczekiwaniu na poniższe funkcje, aby zidentyfikować faktyczną przyczynę zakończenia stanu oczekiwania.
  • ThreadSleep() - Zastępuje użycie funkcji Sleep() dla kodu intra-thread.
  • ThreadWaitForSingleObject() - Zastępuje użycie funkcji WaitForSingleObject() dla kodu intra-thread.
  • ThreadWaitForMultipleObjects() - Zastępuje użycie funkcji WaitForMultipleObjects() dla kodu intra-thread.

Pierwsza funkcja może być używana do sprawdzania lekkich zleceń zakończenia w trakcie długotrwałego przetwarzania zadań. (Na przykład duża kompresja plików). Pozostałe funkcje obsługują przypadek oczekiwania na niektóre zasoby systemowe, takie jak zdarzenia, semafory itp. (Na przykład wątek roboczy oczekujący na nowe żądanie przychodzące z kolejki żądań).