2009-11-09 2 views
9

Мое приложение создает поток и работает в фоновом режиме все время. Я могу только прекратить поток вручную, а не из функции обратного вызова потока. На данный момент я использую TerminateThread(), чтобы убить эту ветку, но это заставляет ее иногда зависать. Я знаю, что есть способ использовать события и WaitForSingleObject(), чтобы поток прекратился изящно, но я не могу найти пример об этом.Прекращение потока изящно не использующего TerminateThread()

Пожалуйста, введите код, указанный на картинке.

ответ

18

TerminateThread - плохая идея, особенно если ваш поток использует объекты синхронизации, такие как мьютексы. Это может привести к невыпущенной памяти и ручкам, а также к взаимоблокировкам, поэтому вы правы, что вам нужно сделать что-то еще.

Как правило, путь, которым заканчивается нить, должен возвращаться из функции, определяющей поток. Основной поток сигнализирует, что рабочий поток выйдет с использованием объекта события или даже простого логического, если он проверен достаточно часто. Если рабочий поток ждет WaitForSingleObject, вам может потребоваться изменить его на WaitForMultipleObjects, где один из объектов - это событие. Основной поток вызовет SetEvent, и рабочий поток проснется и вернется.

Мы действительно не можем предоставить какой-либо полезный код, если вы не покажете нам, что делаете. В зависимости от того, что делает рабочий поток, и как ваш основной поток передает ему информацию, он может выглядеть совсем по-другому.

Кроме того, в рамках [сейчас очень старого] MSVC вам необходимо использовать _beginthreadex вместо CreateThread во избежание утечек памяти в ЭЛТ. См. MSKB #104641.

Update:

Один использование рабочего потока является как «таймер», чтобы сделать некоторые операции на регулярной основе. В наиболее тривиальной форме:

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

Другое использование - это сделать что-то по требованию. В основном то же самое, но с таймаутом, установленным на INFINITE и выполняющим какое-то действие на WAIT_OBJECT_0 вместо WAIT_TIMEOUT. В этом случае вам потребуется два события, один, чтобы сделать нить проснуться и сделать какое-то действие, другой, чтобы сделать его проснуться и выйти:

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

Обратите внимание, что очень важно, чтобы петля сделать что-то разумное, если WFSO/WFMO возвращает ошибку вместо одного из ожидаемых результатов. В обоих примерах, приведенных выше, мы просто рассматриваем ошибку, как будто мы были вынуждены прекратить работу.

Вы можете добиться того же результата в первом примере, закрыв дескриптор события из основного потока, в результате чего рабочий поток получит ошибку от WaitForSingleObject и закроется, но я бы не рекомендовал этот подход.

+0

Функция обратного вызова потока является for (;;), и все. Ничего необычного. поэтому, я повторю вопрос, используя WaitForSingleObject() и SetEvent(), как я могу сигнализировать о моем потоке, который мне нужно закончить? Если я вызову SetEvet (FinishEvent), то WaitForSingleObject() вернет что? 0? 1? -1? – Uri

+0

@Uri Когда вы вызываете 'SetEvent',' WaitForSingleObject' вернет 'WAIT_OBJECT_0' (что равно 0). Когда вы упомянули использование 'WaitForSingleObject', я предположил, что это означало, что вы уже использовали это событие, чтобы сигнализировать рабочему потоку что-то делать (кроме выхода).Также обратите внимание, что в некоторых случаях вам нужно сигнализировать поток wotker для выхода, а затем дождаться, пока он фактически остановится, прежде чем продолжить. (Вы можете использовать 'WaitForSingleObject' для дескриптора потока для этой цели). –

+0

+1 Это ответ, который я бы дал. Делал это так много раз. –

1

Поскольку вы не знаете, что делает нить, нет способа безопасно прекратить поток извне.

Почему, по вашему мнению, вы не можете его прекратить изнутри?

Вы можете создать событие до начала потока и передать его в поток. Вы вызываете SetEvent() на это событие из основного потока, чтобы сигнализировать о прекращении потока, а затем WaitForSingleObject на дескрипторе потока, чтобы дождаться завершения потока.В цикле потоков вы вызываете WaitForSingleObject() на событие, указав тайм-аут 0 (ноль), так что вызов немедленно возвращается, даже если событие не установлено. Если этот вызов возвращает WAIT_TIMEOUT, событие не установлено, если оно возвращает WAIT_OBJECT_0, оно установлено. В последнем случае вы возвращаетесь из функции потока.

Предполагаю, что ваша нить не просто сжигает циклы процессора в бесконечном цикле, но делает некоторые ожидания, может быть, через вызов Sleep(). Если это так, вы можете сделать сон в WaitForSingleObject вместо этого, передав ему тайм-аут.

+1

нить ничего не делает, только проверяя, что определенная программа всегда работает и работает. – Uri

0

Что вы делаете в фоновом режиме? Если вы что-то зацикливаете, вы можете закончить поток внутри себя, имея общий публичный статический объект (например, Boolean), который вы устанавливаете в true из потока переднего плана и что фоновый поток проверяет и выдает чистое значение, когда установлено значение true ,

0

Это пример кода для управления потоками в режиме вилки. Он использует struct Thread как дескриптор потока.

Введем некоторые абстракции структуры дескриптора данных потока:

#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; 
    }; 

Функции для родительского использования резьбы:

  • StartThread() инициализировать эту структуру и запускает новую нить.
  • StopThread() инициирует завершение потока и ждет, пока поток не будет фактически завершен.

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

Этот набор функций, как ожидается, будет использоваться в потоке, запущенном StartThread():

  • IsThreadStopped() - Проверка запроса на завершение. Должно использоваться после ожидания нижеуказанных функций для определения фактической причины окончания состояния ожидания.
  • ThreadSleep() - Заменяет использование Sleep() для внутрипоточного кода.
  • ThreadWaitForSingleObject() - Заменяет использование WaitForSingleObject() для внутрипоточного кода.
  • ThreadWaitForMultipleObjects() - Заменяет использование WaitForMultipleObjects() для внутрипоточного кода.

Первая функция может использоваться для облегчения проверки запроса на завершение при длительной работе. (Например, сжатие большого файла). Остальные функции обрабатывают случай ожидания некоторых системных ресурсов, таких как события, семафоры и т. Д. (Например, рабочий поток ожидает нового запроса, поступающего из очереди запросов).

BOOL IsThreadStopped(struct Thread* CONST thread) 
{ 
    return thread->stop; 
} 

VOID ThreadSleep(struct Thread* CONST thread, DWORD dwMilliseconds) 
{ 
    WaitForSingleObject(thread->event, dwMilliseconds); 
} 

DWORD ThreadWaitForSingleObject(struct Thread* CONST thread, HANDLE hHandle, DWORD dwMilliseconds) 
{ 
    HANDLE handles[2] = {hHandle, thread->event}; 

    return WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds); 
} 

DWORD ThreadWaitForMultipleObjects(struct Thread* CONST thread, DWORD nCount, CONST HANDLE* lpHandles, DWORD dwMilliseconds) 
{ 
    HANDLE* handles = (HANDLE*)malloc(sizeof(HANDLE) * (nCount + 1U)); 
    DWORD status; 

    memcpy(handles, lpHandles, nCount * sizeof(HANDLE)); 

    handles[nCount] = thread->event; 
    status = WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds); 

    free(handles); 

    return status; 
} 
Смежные вопросы