本記事は、COM Advent Calendar 2014 – Qiitaの14日目の記事です。
前回(STAのメソッド呼び出しを見てみる)はSTAだったので、今度はMTAです。
MTAは再入が無い代わりに、スレッドプール(MTA所属)で実行されることがあります。前回同様にコールスタックを見ても良いのですが、こちらはそこまで念入りに確認することもないと思いました。というわけで、今回は簡単にスレッドIDの出力で見てみることにします。
前回のプログラムを少し改造しただけです。
#define _ATL_NO_AUTOMATIC_NAMESPACE #include <iostream> #include <thread> #include <tchar.h> #include <windows.h> #include <atlbase.h> #include <atlcom.h> #include <atlutil.h> class Module : public ATL::CAtlExeModuleT<Module> {}; Module module; ATL::CComGITPtr<ISequentialStream> g1; class ATL_NO_VTABLE Hoge : public ATL::CComObjectRootEx<ATL::CComSingleThreadModel> , public ISequentialStream { public: BEGIN_COM_MAP(Hoge) COM_INTERFACE_ENTRY(ISequentialStream) END_COM_MAP() IFACEMETHOD(Read)( _Out_writes_bytes_to_(cb, *pcbRead) void* pv, _In_ ULONG cb, _Out_opt_ ULONG *pcbRead) override { std::cout << "Read:\t" << std::this_thread::get_id() << std::endl; return E_NOTIMPL; } IFACEMETHOD(Write)( _In_reads_bytes_(cb) const void* pv, _In_ ULONG cb, _Out_opt_ ULONG *pcbWritten) override { return E_NOTIMPL; } }; void worker() { if (FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) return; std::cout << "worker:\t" << std::this_thread::get_id() << std::endl; try { ATL::CComPtr<ISequentialStream> s; ATLENSURE_SUCCEEDED(g1.CopyTo(&s)); char buf; s->Read(&buf, sizeof buf, nullptr); } catch (const ATL::CAtlException& e) { std::clog << ATL::AtlGetErrorDescription(e) << std::endl; } CoUninitialize(); } int main() { if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE))) return 1; std::cout << "Main:\t" << std::this_thread::get_id() << std::endl; { ATL::CComObjectStackEx<Hoge> obj; g1 = &obj; std::thread t(worker); t.join(); g1.Revoke(); } CoUninitialize(); } |
main関数をMTAにし、worker関数のスレッドをSTAにしています。関数workerからs->ReadでMTAへのメソッド呼び出しを行っています。main、worker、Hoge::Readの3ヶ所でスレッドIDを出力させており、実行結果はこんな感じです。
Main: 38252 worker: 37672 Read: 25552
このように、3つとも別々のスレッドです。このように、MTAに対する他アパートメントからの呼び出しはAPIが抱えているスレッドで実行されます。
MSDNライブラリ上では、このスレッドはWindows VistaまではRPC APIのスレッドプール、Windows 7以降はデフォルトのスレッドプールを使うと書かれています。アプリケーションマニフェストのSupportedOS要素での指定で動作が切り替わる項目です。アプリケーション マニフェストの「RPC の既定のスレッド プール」欄をご覧ください。
なお、アプリケーションマニフェストのほか、IGlobalOptionsのCOMGLB_RPC_THREADPOOL_SETTINGでも設定できるようです。
スポンサード リンク |
この記事のカテゴリ
- COM ⇒ MTAのメソッド呼び出しを見てみる
Windows 7で対象のスレッドプールが変わっている事は知りませんでした。濃い記事ありがとうございます!