除了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.
我觉得其他有两个比较重要,跟STA相比,
1. COM系统不会帮助序列号,需要程序员自己来处理并发同步问题;
2. 不需要消息循环。
另外,一个进程里面只能有一个MTA套间,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。如图:
这次的接口名字叫做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\n", 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\n", ::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\n", ::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\n", ::GetCurrentThreadId());
OutputDebugStringW(temp);
{
CComPtr spLine;
spLine.CoCreateInstance(CLSID_MyLine, NULL, CLSCTX_INPROC);
spLine->Draw(CComBSTR(L"red"));
std::vector 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模型,直接起了5个线程来调用Draw。结果如下:
可以很清楚的看到,主线程里面的Draw先完成了。(当然,因为那个时候辅助线程还没起)。
之后的5个线程里面,Draw函数就打乱了。线程2616的Draw函数在sleep的时候,其他线程的Draw函数也在运行。
这5个线程用的是同一个com对象。这个跟STA组件完全不同,STA里面都是串行化的,但是MTA这边就不是串行的了。所以MTA组件需要自己实现同步,不然就乱了。
上面的代码例子是:
1. 主线程创建MTA套间;
2. 主线程创建MTA对象;
3. 辅助线程也初始化成MTA;
那么如果辅助线程初始化成STA或者不初始化又如何?
待续。。。