COMスレッドモデル-MTAインタフェース

4323 ワード

STA以外にもCOMコンポーネントのもう一つの重要なモデルはMTAである.
MTAについてはMSDNを先に読み、 http://msdn.microsoft.com/en-us/library/windows/desktop/ms693421(v=vs.85).aspx
よく読む必要があります.いくつかの重要なルールを列挙します.
• COM provides call synchronization for single-threaded apartments only. 
• Multithreaded apartments do not receive calls while making calls (on the same thread). 
• Multithreaded apartments cannot make input-synchronized calls. 
• Asynchronous calls are converted to synchronous calls in multithreaded apartments. 
• The message filter is not called for any thread in a multithreaded apartment. 

他にも2つは重要だと思いますが、STAと比べると、
1.COMシステムはシリアル番号を助けることができず、プログラマー自身が同時同期問題を処理する必要がある.
2.メッセージループは不要です.
また、1つのプロセスにはMTAスイートルームが1つしかありません.MTAスイートルームには複数のスレッドがあります.MSDNではよく説明されています.
In a multithreaded apartment model, all the threads in the process that have been initialized as free-threaded reside in a single apartment. Therefore, there is no need to marshal between threads. The threads need not retrieve and dispatch messages because COM does not use window messages in this model.

MTAコンポーネント
まずMTAコンポーネントを作成しましょう.簡単です.Threading modelでFreeを選択します.図:
COM线程模型 - MTA接口_第1张图片
今回のインタフェースの名前はIMyLineで、同じように簡単なDraw関数を書きます.
STDMETHODIMP CMyLine::Draw(BSTR color)
{
	// TODO: Add your implementation code here
	WCHAR temp[100] = { 0 };
	swprintf_s(temp, L"IMyLine::Draw, color: %s, tid: %d
", color, ::GetCurrentThreadId()); OutputDebugStringW(temp); std::this_thread::sleep_for(std::chrono::milliseconds(100)); memset(temp, 0, sizeof(temp)); swprintf_s(temp, L"IMyLine::Draw, end, tid: %d
", ::GetCurrentThreadId()); OutputDebugStringW(temp); return S_OK; }

お客様の手順は次のとおりです.

// TestCom.cpp : Defines the entry point for the console application. //

#include "stdafx.h"

#include #include #include #include

#include "../MyCom/MyCom_i.h" #include "../MyCom/MyCom_i.c"

void Test(CComPtr& spLine) {  WCHAR temp[100] = { 0 };  swprintf_s(temp, L"calling thread: %d
", ::GetCurrentThreadId());  OutputDebugStringW(temp);

 HRESULT hr = S_OK;  hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

 spLine->Draw(CComBSTR(L"yellow"));

 CoUninitialize(); }

int _tmain(int argc, _TCHAR* argv[]) {  CoInitializeEx(NULL, COINIT_MULTITHREADED);  WCHAR temp[100] = { 0 };  swprintf_s(temp, L"Main thread: %d
", ::GetCurrentThreadId());  OutputDebugStringW(temp);

 {   CComPtr spLine;   spLine.CoCreateInstance(CLSID_MyLine, NULL, CLSCTX_INPROC);

  spLine->Draw(CComBSTR(L"red"));

  std::vector<:thread> vThreads;   for (int i = 0; i < 5; i++)   {    vThreads.push_back(std::thread(Test, spLine));  // pass a stream instead of com object   }

  for (auto& t: vThreads)   {    t.join();   }  }

 CoUninitialize();

 return 0; }

 


上のコードは簡単です.メインスレッドにはMTAスイートルームが作成され、MTAオブジェクトが作成され、補助スレッドが直接渡されます.(marshalなし)
その後,補助スレッドにおいてもMTAモデルに初期化され,Drawを呼び出すために直接5つのスレッドが作成された.結果は次のとおりです.
COM线程模型 - MTA接口_第2张图片
メインスレッドの中のDrawが先に完成しているのがよく見えます.(もちろん、その時は補助スレッドがまだ起きていなかったので).
その後の5スレッドでDraw関数が狂ってしまいました.スレッド2616のDraw関数はsleepのとき、他のスレッドのDraw関数も実行される.
この5つのスレッドは同じcomオブジェクトを使用しています.これはSTAとは全く異なり、STA内はシリアル化されていますが、MTA側はシリアル化されていません.MTAコンポーネントは自分で同期する必要があります.そうしないと混乱します.
上記のコードの例は、
1.メインスレッドはMTAスイートルームを作成する;
2.メインスレッドはMTAオブジェクトを作成する;
3.補助スレッドもMTAに初期化する.
では、補助スレッドをSTAに初期化するか、初期化しないかはどうでしょうか.
続きます...