C++ 安全地同步COM线程
过去我在多线程方面做了很多工作,但我对COM还是相当陌生的。无论如何,我的问题是: 我创建了一个工作线程,它注册为STA,并创建了一个COM对象。然后工作线程和主线程尝试相互通信。使用C++ 安全地同步COM线程,c++,multithreading,com,boost,synchronization,C++,Multithreading,Com,Boost,Synchronization,过去我在多线程方面做了很多工作,但我对COM还是相当陌生的。无论如何,我的问题是: 我创建了一个工作线程,它注册为STA,并创建了一个COM对象。然后工作线程和主线程尝试相互通信。使用comarshalinterreadinterfaceinstream和CoGetInterfaceAndReleaseStream,我可以让线程调用另一个线程中COM对象的方法 下面是工作线程的外观: void workerThread() { CoInitialize(NULL); MyLib::IFo
comarshalinterreadinterfaceinstream
和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));
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));
但是如何将
boost::thread.join()
和boost::condition.wait()
与MsgWaitForMultipleObjects
混合使用呢?这是可能的,还是我必须做些别的事情来避免竞争条件?您的主线程有一个消息队列(必须是,因为它是STA主机),为什么不直接向它发送消息呢?发布一条用户消息(WM_user+X),您的普通主线程消息泵可以将此用户消息作为通知处理,通知COM对象已将接口封送到流中,并且主线程可以安全地调用CogeInterface和LeaseStream
我必须指出,在您当前的设计中,您的工作线程基本上只不过是运行一个额外的消息泵。主线程对接口上任何方法的任何调用都将被阻塞,等待工作线程从其消息队列中提取消息,处理调用,响应,然后主线程将继续。所有操作的速度至少与将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);
}
}
在主线程中:
// 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));
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));
m_startupDone=CreateEvent(NULL,FALSE,FALSE,NULL);
m_线程开始计数=
//启动线程
m_worker=boost::thread(&workerThread);
m_worker2=boost::thread(&workerThread);
...
//现在等待线程创建COM对象
if(WAIT_OBJECT0!=WaitForSingleObject(m_startupDone,…)
{
//处理诸如超时之类的故障
}
//到目前为止,所有COM对象都已保证创建和封送,并在主目录中全部解组
//这里必须检查所有线程是否真正成功(可以简单到m_stream不为NULL)
//获取接口代理
MyLib::IFooPtr foo;
LPVOID-vp(空);
HRESULT hr=cogetInterface和leaseStream(m_stream,foo.GetIID(),&vp);
如果(成功(hr))foo.Attach(静态施法(vp));
不,我不确定这是我想要的。我正在处理一个遗留的单线程应用程序,试图通过添加线程来缩短启动时间。我意识到我在用启动时间换取一般运行时间。我希望最终能将其提交给MTA,但使用STA作为开发的中间步骤。如果您启动了N个线程,每个线程创建一个STA对象,然后让每个线程在创建接口并将其封送到流中后,立即减少一个计数器(InterlocatedDecrement)。将其递减为0的是最后一个,可以发出事件信号。主线程处于空闲状态,等待一个this事件,当发出信号时,它可以安全地继续并解组所有流。这对传统应用程序逻辑的影响最小,同时允许并行创建N COM对象。您能举个例子吗?我不清楚如何使用PostThreadMessage
和MsgWaitForMultipleObjects
。