Почему поток win32 не выходит автоматически? - PullRequest
6 голосов
/ 10 февраля 2012

Справочная информация:

В моем приложении, написанном на C ++, я создаю рабочий поток, который, в свою очередь, создает два потока, используя CreateThread(). Два потока, которые создает рабочий поток, общаются со Службой WCF через клиента, который реализован с использованием API веб-служб Windows , который предлагает интерфейс программирования приложений C / C ++ (API) для создания веб-служб и клиентов на основе SOAP , Мое приложение реализует только клиент, использующий этот API.

Проблема:

Проблема, с которой я сталкиваюсь, заключается в том, что все остальные потоки завершаются корректно, за исключением рабочего потока, как вы можете видеть на рисунке ниже, что WorkerThreadProc не использует циклы ЦП, но не завершает работу. Есть также несколько других запущенных потоков, которые созданы не мной, а средой выполнения.

Состояния потоков следующие (как сообщает ProcessExplorer ):

  • WorkerThreadProc находится в Ожидание: WrUserRequest состояние.
  • wWinMainCRTStartup находится в Ожидание: пользовательский запрос состояние.
  • Все TpCallbackIndependent находятся в Ожидание: WrQueue состояние.

Чего они ждут? Какие могут быть возможные причины, которые мне нужно изучить? Кроме того, в чем разница между WrUserRequest и UserRequest ? А что означает WrQueue ? Я абсолютно не знаю, что здесь происходит.

enter image description here


Вот мой код WorkerThreadProc. Я удалил все операторы логирования, кроме последнего в нижней части функции:

DWORD WINAPI WorkerThreadProc(PVOID pVoid)
{

    //Initialize GDI+
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR           gdiplusToken;

    Status status = GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
    if ( status != Status::Ok )
    {
        return 1;
    }

    GuiThreadData *pGuiData = (GuiThreadData*)pVoid;

    auto patternIdRequestQueue= new PatternIdRequestQueue();
    auto resultQueue = new ResultQueue();

    auto patternManager = new PatternManager(patternIdRequestQueue);
    LocalScheduler *pScheduler = new LocalScheduler(resultQueue, patternManager);

    bool bInitializationDone = pScheduler->Initialize(pGuiData->m_lpCmdLine);
    if ( !bInitializationDone )
    {
        return 0;
    }

    //PatternIdThread
    PatternIdThread patternIdThread(patternIdRequestQueue);
    DWORD dwPatternIdThreadId;
    HANDLE hPatternIdThread = CreateThread(NULL, 0, PatternIdThreadProc, &patternIdThread, 0, &dwPatternIdThreadId);

    ResultPersistence resultPersistence(resultQueue);
    DWORD dwResultPersistenceThreadId;
    HANDLE hResultPersistenceThread = CreateThread(NULL, 0, ResultPersistenceThreadProc, &resultPersistence, 0, &dwResultPersistenceThreadId);

    pScheduler->ScheduleWork(pGuiData->m_hWnd, pGuiData->m_hInstance, ss.str());

    pScheduler->WaitTillDone();
    patternIdThread.Close();
    resultPersistence.Close();

    delete pScheduler; 

    //Uninitialize GDI+
    GdiplusShutdown(gdiplusToken);

    dwRet = WaitForSingleObject(hPatternIdThread, INFINITE);
    CloseHandle(hPatternIdThread);

    dwRet = WaitForSingleObject(hResultPersistenceThread,INFINITE);
    CloseHandle(hResultPersistenceThread);

    SendMessage(pGuiData->m_hWnd, WM_CLOSE, 0, 0);

    //IMPORTANT : this verbose message is getting logged!
    T_VERBOSE(EvtSrcInsightAnalysis, 0, 0, "After sending message to destroy window");

    delete patternManager;
    delete patternIdRequestQueue;
    delete resultQueue;
    return 0;
}

Пожалуйста, смотрите макрос T_VERBOSE, он используется для записи подробного сообщения. Я вижу, что сообщение регистрируется, но поток не завершается!


EDIT:

Я только что прокомментировал следующую строку в моем WorkerThreadProc, затем рабочий поток завершился изящно!

SendMessage(pGuiData->m_hWnd, WM_CLOSE, 0, 0);

Значит ли это, что SendMessage является виновником? Зачем ему блокировать поток вызывающего потока?

1 Ответ

3 голосов
/ 10 февраля 2012

Если мы посмотрим на документы для SendMessage, вы увидите небольшую цитату:

Чтобы отправить сообщение и немедленно вернуться, используйте функцию SendMessageCallback или SendNotifyMessage.Чтобы отправить сообщение в очередь сообщений потока и немедленно вернуться к нему, используйте функцию PostMessage или PostThreadMessage.

и это:

Сообщения, отправленные между потоками, обрабатываются только тогда, когдапринимающий поток выполняет код поиска сообщения.Отправляющий поток блокируется, пока принимающий поток не обработает сообщение.Тем не менее, отправляющий поток будет обрабатывать входящие сообщения без очереди, ожидая обработки своего сообщения.Чтобы предотвратить это, используйте SendMessageTimeout с установленным SMTO_BLOCK.Для получения дополнительной информации о сообщениях без очереди см. Сообщения без очереди.

, поэтому из этого мы можем видеть, что SendMessage будет блокироваться до тех пор, пока сообщение не будет обработано, что может как-то привести к тупику в вашем коде, так какmsgproc не находится в вашем рабочем потоке, что приводит к переключению контекста (которое срабатывает только тогда, когда очередь потока перекачивается на сообщения).Попробуйте использовать PostMessage, который немедленно возвращается.

РЕДАКТИРОВАТЬ: здесь также есть небольшая информация здесь о блокировках сообщений с SendMessage

...