簡體   English   中英

安全地同步COM線程

[英]Safely synchronizing a COM thread

我過去在多線程方面做了很多,但我對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 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 這是可能的,還是我必須做一些其他事情以避免競爭條件?

你的主線程有一個消息隊列(必須是,因為是一個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));

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM