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);
此代码启动我的线程,但线程无法安全退出,因此等待。这段代码有什么问题?
答案 0 :(得分:2)
问题不在此代码中,尽管它违反了核心COM要求。当你不再使用它们时,你应该释放接口指针,调用IUnknown :: Release(),并且公寓线程的线程必须引发一个消息循环。特别是消息循环很重要,当单线程对象(如浏览器)的所有者线程没有被泵送时,你会遇到死锁。
CoUninitialize()被强制清除spDoc3包装的接口指针,因为你自己没有这样做。从代码中可以清楚地看到,接口指针的所有者实际上在另一个线程上运行,这通常要记住,因为这几乎打败了启动自己的工作线程的点。创建自己的STA线程并不能解决这个问题,它仍然是错误的线程。
因此代理需要上下文切换到拥有浏览器对象的公寓。由于硬件要求此公寓泵送消息循环,以便可以在正确的线程上调度调用,以便安全地调用Release()函数。当你的程序关闭时,这个线程不会再输出消息了。您应该能够在调试器中看到的东西,在Debug + Windows + Threads窗口中找到所有者线程,看看它在做什么。
死锁是常见的结果。修复它的唯一好方法是以正确的顺序关闭线程,这个必须在拥有浏览器对象的线程之前关闭。当线程具有这样的相互依赖性时,干净地关闭多线程程序可能非常困难。 C ++ 11 std :: quick_exit()补充背后的灵感。