Dispatcher
スレッドは常時3つあります。
1 呼び出し側のスレッド
2 呼び出される側のスレッド
3 2の結果を返すスレッド
1のスレッドを起こす呼び出し側は大抵WindowMessageと同じGUIスレッド。
2はGUIと異なるスレッド。独自のスタックを持ち重い関数を処理します。
3は何もしなければ2と同じスレッド上になりますが、GUIでスレッドの終了を示すためにはスレッド変換(Dispatcher)してGUIスレッドにしなければなりません。(3を意識しないとC++/CXでハマります)
このDispatcherを作成します。
WndProcにWM_MY_DISPATCHER処理を下のように加えました。汎用的に使えます。
#define WM_MY_DISPATCHER (WM_APP+100)
typedef std::functionDisptcherDelegate; WndProc内
switch(message) {
...
case WM_MY_DISPATCHER:
{
DisptcherDelegate& f = *(DisptcherDelegate*)wParam;
f(hWnd, (void*)lParam);
}
break;
...
return 0;
}
上の関数fはメッセージ処理で実行されるのでGUIスレッドになります。
スレッドを作成実行は以下。concurrency::create_taskを使用してます。
(concurrency::create_task、実はなんだかよくわかないので無視されがちな関数ではないかと思うのですが、
知らなくてもスレッドは作成できますし)
重い処理とthenは独自のスレッドで実行され、スレッドIDはtidとなります。
#include
void FooFunc(HWND hWnd)
{
DWORD gui_thread_id = ::GetCurrentThreadId(); // スレッドIDの取得concurrency::create_task([hWnd,gui_thread_id](){
Sleep(1000);// ... 重い処理
}).then( [hWnd,gui_thread_id](){
static DisptcherDelegate Complete;struct Temp
{
DWORD gid;
DWORD tid;
};Temp* t = new Temp();
t->gid = gui_thread_id;
t->tid = ::GetCurrentThreadId();
Complete = [](HWND,void* p){
Temp* t = (Temp*)p;
_ASSERT( t->gid == ::GetCurrentThreadId() );
_ASSERT( t->tid != ::GetCurrentThreadId() );// ここでメッセージボックスなどのGUIに終了メッセージを出す。
delete t;
};PostMessage( hWnd, WM_MY_DISPATCHER, (WPARAM)&Complete, (LPARAM)t );
});
}
Complete内はWndProcのGUIスレッドで走るためgidと同じになります。
_ASSERTのValidationは無事通過します。
C++/CXのスレッド処理スタイルを古いスタイルで書いてみました。