2011-04-18 71 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