0
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);

このコードは私のスレッドを開始しますが、スレッドは安全に終了できないため、待機します。このコードの何が問題になっていますか?

4

1 に答える 1

2

問題はこのコードにはありませんが、コアCOM要件に違反しています。これは、IUnknown :: Release()を呼び出して、インターフェイスポインターを使用しなくなったときに解放する必要があり、アパートメントスレッドスレッドがメッセージループをポンピングする必要があることを示しています。特にメッセージループは重要です。シングルスレッドオブジェクト(ブラウザなど)の所有者スレッドがポンピングしていない場合、デッドロックが発生します。

CoUninitialize()は、これを自分で行わなかったため、spDoc3によってラップされたインターフェイスポインターを強制的にクリーンアップします。インターフェースポインターの所有者が実際に別のスレッドで実行されていることはコードから明らかです。これは、独自のワーカースレッドを開始するという点をかなり損なうため、一般的に覚えておくべきことです。独自のSTAスレッドを作成してもこれは修正されませんが、それでも間違ったスレッドです。

したがって、プロキシはブラウザオブジェクトを所有するアパートにコンテキストスイッチする必要があります。このアパートメントは、Release()関数を安全に呼び出すために、適切なスレッドで呼び出しをディスパッチできるように、メッセージループをポンピングするという厳しい要件があります。プログラムがシャットダウンしているときに、このスレッドがメッセージを送信しなくなる可能性が非常に高くなります。デバッガーで確認できるはずの何かを、[デバッグ+ウィンドウ+スレッド]ウィンドウで所有者スレッドを見つけて、それが何をしているのかを確認します。

デッドロックは一般的な結果です。これを修正する唯一の良い方法は、正しい順序でスレッドをシャットダウンすることです。これは、ブラウザオブジェクトを所有するスレッドの前にシャットダウンする必要があります。スレッドにこのような相互依存関係がある場合、マルチスレッドプログラムをクリーンにシャットダウンすることは非常に難しい場合があります。C ++ 11 std :: quick_exit()の追加の背後にあるインスピレーション。

于 2013-03-25T11:19:56.723 に答える