过去我在多线程方面做了很多,但我对COM很新。无论如何,这是我的问题:
我创建了一个工作线程,它注册为STA,并创建一个COM对象。然后工作线程和主线程尝试相互通信。使用CoMarshalInterThreadInterfaceInStream
和CoGetInterfaceAndReleaseStream
,我可以让线程在另一个线程中的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 Apartment的内容正确同步。据我所知,从阅读msdn,这似乎是正确的做事方式。现在主线程可以使用它的代理来调用我的COM对象上的方法,而工作线程将通过消息队列接收这些调用,并正确地调度它们。
然而,如何同步这些线程?
显然,在这种情况下,我希望主线程等待调用CoGetInterfaceAndReleaseStream
,直到工作线程通过CoMarshalInterThreadInterfaceInStream
创建了该流。但我怎么能安全地做到这一点?
从MSDN开始,我应该使用类似MsgWaitForMultipleObjects
的内容,因此我可以等待my_condition或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
混合?这是否可能,或者我是否必须做其他事情以避免竞争条件?
答案 0 :(得分:3)
你的主线程有一个消息队列(必须是,因为是一个STA主机),为什么不简单地向它发帖子,PostThreadMessage
?发布用户消息(WM_USER + X),您的普通主线程消息泵可以处理此用户消息,作为COM对象已将接口封送到流中的通知,并且主线程可以安全地调用CoGetInterfaceAndReleaseStream
。
我必须要说出来,尽管使用您当前的设计,您的工作线程基本上只是运行一个额外的消息泵。从主线程对您的接口上的任何方法的任何调用都将阻塞,等待工作线程从其消息队列中获取消息,处理该调用,响应,然后主线程将恢复。所有操作至少与在主线程中托管COM对象一样慢,加上COM在两个STA之间来回编组的开销。基本上,由于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));