为啥win32线程不自动退出?

Posted

技术标签:

【中文标题】为啥win32线程不自动退出?【英文标题】:Why win32 thread doesn't exit automatically?为什么win32线程不自动退出? 【发布时间】:2012-02-10 11:33:02 【问题描述】:

背景:

在我用 C++ 编写的应用程序中,我创建了一个工作线程,该工作线程又使用 CreateThread() 创建了两个线程。工作线程创建的两个线程通过客户端与 WCF 服务通信,该客户端使用 Windows Web Services API 实现,该客户端提供 C/C++ 应用程序编程接口 (API),用于构建基于 SOAP 的 Web 服务和客户端。我的应用程序使用此 API 实现客户端。

问题:

我面临的问题是所有其他线程都正常退出,除了工作线程,正如您自己看到的那样,在下图中WorkerThreadProc 不使用 CPU 周期但它没有退出。还有一些其他线程不是我创建的,而是运行时创建的。

线程状态如下(ProcessExplorer报道):

WorkerThreadProc 处于 Wait:WrUserRequest 状态。 wWinMainCRTStartup 处于 Wait:UserRequest 状态。 所有TpCallbackIndependent都处于Wait:WrQueue状态。

他们还在等什么?我需要调查哪些可能的原因?另外,WrUserRequestUserRequest 有什么区别? WrQueue 是什么意思?我完全不知道这里发生了什么。


这是我的 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 宏,它用于记录详细消息。我看到消息正在被记录,但线程没有退出!


编辑:

我刚刚在WorkerThreadProc 中注释了以下行,然后工作线程优雅地退出了!

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

这是否意味着SendMessage 是罪魁祸首?为什么会阻塞调用线程?

【问题讨论】:

你是在线程句柄退出后调用 CloseHandle() 吗?您应该使用 WaitForSingleObject() 来等待线程完成。 @infact:当然,是的!是的,我正在使用WaitForSingleObject 等待线程完成! 你为什么不使用像 boost::thread 这样被证明可以正常工作(甚至是可移植的)的抽象? @PlasmaHH:我不需要便携性。另外,你的意思是win32线程不好(事实证明是这样)? boost::thread 内部使用什么? @PlasmaHH:如果我提出有关boost::thread 的问题怎么办?我觉得你的论点很弱。 【参考方案1】:

如果我们查看SendMessage 的文档,您会看到这个小引语:

要发送消息并立即返回,请使用 SendMessageCallback 或 SendNotifyMessage 函数。将消息发布到线程的消息 排队并立即返回,使用 PostMessage 或 PostThreadMessage 功能。

还有这个:

线程之间发送的消息只有在接收时才被处理 线程执行消息检索代码。发送线程被阻塞 直到接收线程处理消息。然而,发送 线程将在等待它的同时处理传入的非排队消息 要处理的消息。为防止这种情况,请将 SendMessageTimeout 与 SMTO_BLOCK 设置。有关非排队消息的更多信息,请参阅 非排队消息。

所以从这里我们可以看到SendMessage 将阻塞直到消息被处理,这可能会导致代码中的死锁,因为 msgproc 不驻留在您的工作线程中,从而导致上下文切换(仅当线程的队列被抽出消息时触发)。尝试使用PostMessage,它会立即返回。

编辑:here 也有关于来自SendMessage 的消息死锁的一小段信息

【讨论】:

在我自己发现了SendMessage() 的问题后(​​正如我在我的问题中发布的那样),我阅读了 MSDN 上的文档(您已将其发布为答案),但我不是得到这就是为什么SendMessage 没有返回。它不返回意味着接收线程不处理消息,这反过来意味着如果我使用PostMessage,那么即使那样它也不会处理消息。虽然,它会解决阻塞线程的问题,但真正的问题(我们现在知道)仍然存在,那就是:消息没有被处理! @Nawaz:使用 Spy++ 之类的东西会更好地解决这部分问题,我也遇到了类似的问题,WM_CLOSE 消息未被处理,将其追溯到另一​​个返回错误值的窗口消息,导致将WM_CLOSE 消息从队列中删除,因此无法处理

以上是关于为啥win32线程不自动退出?的主要内容,如果未能解决你的问题,请参考以下文章

在使用 std::unique_ptr 在退出作用域块时自动释放内存的情况下,为啥不直接使用堆栈呢?

为啥网站后台登录总是过不长时间就自动退出重新登录

nginx在 window下 自动退出 php-cgi

C# WinForm 不抱错,自动退出的情况

解决win2003远程退出后系统自动注销的问题

dubbo 服务启动之后自动退出,为啥?