2011-04-18 70 views
3

過去我已經使用多線程完成了很多工作,但我對COM還是比較陌生的。無論如何,這是我的問題:安全地同步COM線程

我創建一個工作線程,它註冊爲一個STA,並創建一個COM對象。然後工作者線程和主線程嘗試相互通信。使用CoMarshalInterThreadInterfaceInStreamCoGetInterfaceAndReleaseStream,我可以讓線程調用其他線程中COM對象的方法。

這裏的工作線程的樣子:

void workerThread() 
{ 
    CoInitialize(NULL); 
    MyLib::IFooPtr foo = ...; // create my COM object 

    // Marshall it so the main thread can talk to it 
    HRESULT hr = CoMarshalInterThreadInterfaceInStream(foo.GetIID(), 
                foo.GetInterfacePtr(), 
                &m_stream); 
    if (FAILED(hr)) { 
    // handle failure 
    } 

    // begin message loop, to keep this STA alive 
    MSG msg; 
    BOOL bRet; 
    while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) 
    { 
    if (bRet == -1) break; 

    DispatchMessage(&msg); 
    } 
} 

在主線程:

// launch the thread 
m_worker = boost::thread (&workerThread); 

// get the interface proxy 
MyLib::IFooPtr foo; 
LPVOID vp (NULL); 
HRESULT hr = CoGetInterfaceAndReleaseStream(m_stream, foo.GetIID(), &vp); 
if (SUCCEEDED(hr)) foo.Attach(static_cast<MyLib::IFoo*>(vp)); 

這將創建對象(這需要一段時間來初始化),並允許主線程與之交談,一切都與COM公寓的東西正確同步。據我所知,讀取MSDN,這似乎是正確的做事方式。現在主線程可以使用它的代理來調用我的COM對象上的方法,並且工作線程將通過消息隊列接收這些調用,並正確調度它們。

但是,同步這些線程呢?

很明顯,在這種情況下,我希望主線程等待調用CoGetInterfaceAndReleaseStream,直到工作線程通過CoMarshalInterThreadInterfaceInStream創建該流爲止。但我怎麼能安全地做到這一點?

MSDN,我應該用什麼樣MsgWaitForMultipleObjects,所以我可以等待my_condition OR new_message_arrived,然後我可以這樣做:

// verbatim from msdn 
while (TRUE) 
{ 
    // wait for the event and for messages 
    DWORD dwReturn = ::MsgWaitForMultipleObjects(1, 
        &m_hDoneLoading, FALSE, INFINITE, QS_ALLINPUT); 

    // this thread has been reawakened. Determine why 
    // and handle appropriately. 
    if (dwReturn == WAIT_OBJECT_0) 
    // our event happened. 
    break ; 
    else if (dwReturn == WAIT_OBJECT_0 + 1) 
    { 
    // handle windows messages to maintain 
    // client liveness 
    MSG msg ; 
    while(::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) 
     ::DispatchMessage(&msg) ; 
    } 
} 

但我怎麼混boost::thread.join()boost::condition.wait()MsgWaitForMultipleObjects?這甚至是可能的,還是我必須做其他事情來避免競爭狀況?

回答

3

您的主線程有一個消息隊列(必須,因爲是一個STA主機),爲什麼不簡單地發佈消息呢,PostThreadMessage?發佈用戶消息(WM_USER + X),並且正常的主線程消息泵可以處理此用戶消息,作爲COM對象將接口封送到流中並且主線程可安全撥打CoGetInterfaceAndReleaseStream的通知。

我必須通過你的當前設計調用你的工作線程,基本上只不過是運行一個額外的消息泵。任何從主線程調用接口上的任何方法都會阻塞,等待工作線程從其消息隊列中接收消息,處理該調用並作出響應,然後主線程將恢復。所有的操作至少與主線程中託管COM對象一樣慢,加上兩個STA之間來回COM封送處理的開銷。基本上,兩個線程之間沒有併發性,因爲COM STA的工作方式。你確定這是你想要的嗎?

編輯

(省略一堆像線程的數目,超時處理,爲每個工人等等等等流/ IID/CLSID的分配細節)

。H:

HANDLE m_startupDone; 
volatile int m_threadStartCount; 

工作者線程:

void workerThread() 
{ 
    CoInitialize(NULL); 
    MyLib::IFooPtr foo = ...; // create my COM object 

    // Marshall it so the main thread can talk to it 
    HRESULT hr = CoMarshalInterThreadInterfaceInStream(foo.GetIID(), 
                foo.GetInterfacePtr(), 
                &m_stream); 
    if (FAILED(hr)) { 
    // handle failure 
    // remember to decrement and signal *even on failure* 
    } 

    if (0 == InterlockedDecrement(&m_threadStartCount)) 
    { 
    SetEvent (m_startupDone); 
    } 

    // begin message loop, to keep this STA alive 
    MSG msg; 
    BOOL bRet; 
    while((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) 
    { 
    if (bRet == -1) break; 

    DispatchMessage(&msg); 
    } 
} 
在主線程

m_startupDone = CreateEvent (NULL, FALSE, FALSE, NULL); 
m_threadStartCount = <number of workerthreads> 

// launch the thread(s) 
m_worker = boost::thread (&workerThread); 
m_worker2 = boost::thread (&workerThread); 
... 

// now wait for tall the threads to create the COM object(s) 
if (WAIT_OBJECT0 != WaitForSingleObject(m_startupDone, ...)) 
{ 
    // handle failure like timeout 
} 
// By now all COM objects are guaranteed created and marshaled, unmarshall them all in main 
// here must check if all threads actually succeeded (could be as simple as m_stream is not NULL) 

// get the interface proxy 
MyLib::IFooPtr foo; 
LPVOID vp (NULL); 
HRESULT hr = CoGetInterfaceAndReleaseStream(m_stream, foo.GetIID(), &vp); 
if (SUCCEEDED(hr)) foo.Attach(static_cast<MyLib::IFoo*>(vp)); 
+0

不,我不知道這是我想要的。我正在處理遺留的單線程應用程序,試圖通過添加線程來縮短啓動時間。我意識到我正在爲一般運行時間交易啓動時間。我希望最終能夠將它交給MTA,但將STA用作開發的中間步驟。 – Tim 2011-04-18 22:12:11

+0

如果已經啓動了N個線程,每個線程創建一個STA對象,那麼每創建一個線程並將接口封裝到流中時,遞減一個計數器(InterlockedDecrement)。將其遞減爲0的是最後一個,並且可以發信號通知事件。主線程處於空閒狀態,等待這個事件,當它被髮信號時,它可以安全地繼續前進並解組所有流。這對遺留應用邏輯的影響最小,同時允許並行創建N個COM對象。 – 2011-04-18 22:20:52

+0

你能舉一個例子嗎?我不清楚如何使用PostThreadMessage和MsgWaitForMultipleObjects。 – Tim 2011-04-18 22:27:40