Прекращение потока изящно, не используя TerminateThread ()

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

Пожалуйста, код нужен здесь.

Ответы на вопрос(4)

Решение Вопроса

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

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

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

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

Обновить:

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

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 и уйти, но я бы неЯ рекомендую такой подход.

 Harry Johnston28 апр. 2014 г., 03:39
Современные версии C runtime don 'не проявлять утечку памяти, еслии то и другое выполняются следующие условия: среда выполнения C связана статически, а не как DLL; и приложение работает в Windows XP или более ранней версии. (Это потому, что применяются два отдельных обходных пути, один на основе DllMain и один на основе FlsCallback.)
 Uri09 нояб. 2009 г., 20:12
О, неважно. Я закрывал ручку для событиядо нить успела что-нибудь сделать. Спасибо за помощь
 ur.09 нояб. 2009 г., 18:21
+1 Это ответ, который я бы дал. Делал это так много раз.
 Uri09 нояб. 2009 г., 19:12
Поэтому я создаю событие с помощью CreateEvent (), затем вызываю SetEvent () и внутри функции обратного вызова для своего потока я вызываю WaitForSingleObject (), но это всегда возвращает ошибку 6, недействительный дескриптор. Я передаю дескриптор, возвращенный CreateEvent ()
 Tim Sylvester09 нояб. 2009 г., 20:14
@Uri В твоей главной теме ты звонишьSetEvent а потомCloseHandle? Если это так, дескриптор закрывается до того, как рабочий поток получает сигнал события. Технически, поток получает ошибку и останавливается, что событие делает то, чтоs предназначен для, но "правильный" Чтобы продолжить, установите событие, затем дождитесь остановки потока, прежде чем закрывать дескриптор события.
 Tim Sylvester09 нояб. 2009 г., 20:20
@bdonlan Интересно, я нене вижу этого. Насколько я понимаю проблему, это 'нефиксированный" и никогда не будет, потому что этоне ошибка. Это'Возможно, у MS есть обходной путь в более поздних версиях, но этоБолее вероятно, что им просто не удалось обновить статью базы знаний. Я бы еще использовал_beginthreadex если вы не найдете что-то конкретно говорящее иначе.
 bdonlan09 нояб. 2009 г., 19:05
В статье MSKB, на которую вы ссылаетесь, указано, что она применима только до MSVC 6.0 - эта утечка все еще происходит в современных версиях?
 Uri09 нояб. 2009 г., 18:02
Функция обратного вызова потока - это for (;;), которая 'все. Ничего необычного. так что я'Я повторю вопрос, используя WaitForSingleObject () и SetEvent (), как я могу сообщить моему потоку, что мне нужно закончить? Если я вызову SetEvet (FinishEvent), то WaitForSingleObject () вернет что? 0? 1? -1?
 Tim Sylvester09 нояб. 2009 г., 18:10
@Uri Когда ты звонишь,SetEventWaitForSingleObject вернусьWAIT_OBJECT_0 (который равен 0). Когда вы упомянули использованиеWaitForSingleObject Я предположил, что это означает, что вы уже используете событие, чтобы сигнализировать рабочему потоку что-то сделать (кроме выхода). Также обратите внимание, что в некоторых случаях вам нужно дать сигнал потоку wotker выйти, а затем дождаться его остановки, прежде чем продолжить. (Ты можешь использоватьWaitForSingleObject на ручке нити для этого).

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

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

Как вы думаете, почему вы не можете прекратить это изнутри?

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

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

 Uri09 нояб. 2009 г., 18:03
поток ничего не делает, только проверяет, что конкретная программа всегда запущена и работает.

ет struct Thread в качестве дескриптора потока.

Позволять's введем некоторую абстракцию структуры данных дескриптора потока:

    #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;
    };
</windows.h>

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

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

Ваш ответ на вопрос