CoGetInterfaceAndReleaseStream让我的线程挂起

时间:2013-03-25 09:43:06

标签: multithreading visual-c++ com

UINT __stdcall CExternal::WorkThread( void * pParam)
{
    HRESULT hr;
    CTaskBase* pTask;
    CComPtr<IHTMLDocument3> spDoc3;
    CExternal* pThis = reinterpret_cast<CExternal*>(pParam);

    if (pThis == NULL)
        return 0;

    // Init the com
    ::CoInitializeEx(0,COINIT_APARTMENTTHREADED);
    hr = ::CoGetInterfaceAndReleaseStream(
        pThis->m_pStream_,
        IID_IHTMLDocument3,
        (void**)&spDoc3);

    if(FAILED(hr))
        return 0;

    while (pThis->m_bShutdown_ == 0) 
    {
        if(pThis->m_TaskList_.size()) 
        {
            pTask = pThis->m_TaskList_.front();
            pThis->m_TaskList_.pop_front();

            if(pTask) 
            {
                pTask->doTask(spDoc3); //do my custom task
                delete pTask;
            }
        } 
        else
        {
            Sleep(10);
        }
    }

    OutputDebugString(L"start CoUninitialize\n");
    ::CoUninitialize(); //release com
    OutputDebugString(L"end CoUninitialize\n");
    return 0;
}

上面的代码让我的线程挂起,唯一的输出是“start CoUninitialize”。

m_hWorker_ = (HANDLE)_beginthreadex(NULL, 0, WorkThread, this, 0, 0);

此代码启动我的线程,但线程无法安全退出,因此等待。这段代码有什么问题?

1 个答案:

答案 0 :(得分:2)

问题不在此代码中,尽管它违反了核心COM要求。当你不再使用它们时,你应该释放接口指针,调用IUnknown :: Release(),并且公寓线程的线程必须引发一个消息循环。特别是消息循环很重要,当单线程对象(如浏览器)的所有者线程没有被泵送时,你会遇到死锁。

CoUninitialize()被强制清除spDoc3包装的接口指针,因为你自己没有这样做。从代码中可以清楚地看到,接口指针的所有者实际上在另一个线程上运行,这通常要记住,因为这几乎打败了启动自己的工作线程的点。创建自己的STA线程并不能解决这个问题,它仍然是错误的线程。

因此代理需要上下文切换到拥有浏览器对象的公寓。由于硬件要求此公寓泵送消息循环,以便可以在正确的线程上调度调用,以便安全地调用Release()函数。当你的程序关闭时,这个线程不会再输出消息了。您应该能够在调试器中看到的东西,在Debug + Windows + Threads窗口中找到所有者线程,看看它在做什么。

死锁是常见的结果。修复它的唯一好方法是以正确的顺序关闭线程,这个必须在拥有浏览器对象的线程之前关闭。当线程具有这样的相互依赖性时,干净地关闭多线程程序可能非常困难。 C ++ 11 std :: quick_exit()补充背后的灵感。