2008年07月21日

スレッドとタイマーとイベント その1 別スレッドからメインスレッドのAPCキューにポストする。

複数スレッドを同期させるのにイベントとAPCキューがあることが分かった。
前回SetWaitableTimerをメインスレッドで呼び出しメッセージループの隙間で実行することが出来た。
いわばWM_TIMERの高精度版ともいえる。
イベントの待機待ちではなくてキューを拾うわけだから途中に動作が入ってもよいわけだ。
ということは別スレッドから完了ルーチンをメインスレッドにポストして実行させることもできるはずだと考えた。
用途としては別スレッドでブロックする命令を実行して終了したらコールバックを実行させたいという向きで
基本的にキューを自前実装すればAPIに頼らなくてもよさそうな内容だ。
しかし、これができるとWindows98も含めてシンプルなコーディングができるのでテストしてみた。

まずキューにポストする関数としてはSetWaitableTimerがあるが同一スレッドに限定される。
別スレッドからAPCキューにポストするには

「QueueUserAPC」

を使うようだ。

この命令は

QueueUserAPC(@APC_func1,thread,param);

と極めてシンプルで
コールバックも


Procedure APC_func1(param:LongWord)stdcall;

のような形でよい。paramにハンドルとか構造体のポインタとか渡せばいいわけだ。

ここで一つ問題が生じた。
CreateThreadで作ったスレッドはハンドルが明確だが
メインスレッドのハンドルは不明だ。

そこで調べたところ

「GetCurrentThread」

で現スレッドのハンドルが取得できると思ったのだが
どのスレッドでこの関数を呼び出しても同じ値になる。
どうも呼び出しスレッドのみで有効なようだ。
そこで

「DuplicateHandle」

で複製すればよいらしいことが分かった。
ここで得られたハンドルは開放する必要があるらしいのでハンドルリストを使うことにした。


DuplicateHandle(GetCurrentProcess,GetCurrentThread,
GetCurrentProcess,@m_object[5],0,FALSE,
2//DUPLICATE_SAME_ACCESS
);
App_Thread:=m_object[5];


App_Threadという変数は作業用だ。
GetCurrentProcessとGetCurrentThreadを使ってシンプルに出来た。

テストはpeekmessageのところの待機を

SleepEx(50,true);



Sleep(50);

で切り替えて行った。

呼び出しは別スレッドのTimesetEventのコールバックを用いて


procedure MM_func9(uTimerID, uMessage: UINT;
dwUser, dw1, dw2: DWORD) stdcall;
begin
QueueUserAPC(@APC_func1,App_Thread,dwuser);
end;


呼び出しは

timeSetEvent(2000,0,@MM_func9,127,0);

※127は適当な識別用の値
とした。

期待どうりSleepExにするとコールバックが呼ばれSleepにすると沈黙した。
ちなみにMM_func9内でGetCurrentThreadでコールしたところうまく呼べた
timeSetEventは奥が深い。

現状
m_object[0]:WaitableTimer用
m_object[1]:m_object[3]始動用
m_object[2]:m_object[3]待機用
m_object[3]:常駐別スレッド
m_object[4]:臨時スレッド用
m_object[5]:メインスレッド

というスレッドとイベントを使用しているのだが
常駐別スレッドへのポストでもうまくいった。

実際にコールバックをどこでやられせるかという問題は
処理の内容しだいだが
現在の実験プロセスはアプリの根幹部分に関わるので
単なる知識欲ということではなくて有効に活用できそうだ。


posted by Xo_ox at 21:07| Comment(0) | Win出直し | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は180日以上新しい記事の投稿がないブログに表示されております。