2008年07月16日

時間計測とタイマー その6 タイマー実行中の再呼び出し

SetWaitableTimerなりtimeSetEventなりでタイマーを使う上で気になるのが処理落ち時の挙動だが
タイマールーチンにsleepをいれてどうなるか調べてみた。
今回はコールバックルーチンに間隔以上のsleepをいれてみた。
結果としてSetWaitableTimerもtimeSetEventもコールバックでsleep等で長時間ブロックすると
次のコールバックは実行されないで遅延した。
そして嫌なことにこれは命令実行中に再度呼び出されないということではない。

このことに気づいたのはtimeSetEventのコールバックで
SleepExを使ってWaitableTimerのシグナルを待機したところ
実行中には次の命令を受け付けないことから気がついた。
1ショット命令なのでIDが同じになりスレッドが同一になるのだと思ったら
IDは呼出し毎に違う値だった。

そこで

var
app_cnt1:LongInt;


なる変数を用意して各コールバックルーチンはこれが0だったら強制停止するようにしてみた。

メニューからのコマンドによる強制停止は

app_cnt1:=0;


でよい。

マルチメディアタイマーはコールバックの筆頭に

if (app_cnt1=0) then timeKillEvent(uTimerID);
app_cnt1:=app_cnt1-1;


を入れる。

待機タイマー(WaitableTimer)の方は

if (app_cnt1=0) then CancelWaitableTimer(WTimer1);
app_cnt1:=app_cnt1-1;


でとまる。真面目に組む場合はカウンターの構造体変数に保持すればよい。

ところで待機タイマーを繰り返しで使う時

SetWaitableTimer(WTimer1, WTimer1t,2000, @WMTimeCallBack2, Pointer(dwuser), TRUE);



となり3番目の値が繰り返し間隔なのだが単位がmsになる。
せっかく開始が100ns単位でも繰り返しが1msではあまり意味がない。
前エントリーで制御とか書いたがこれじゃ使えない。

で、ここまで準備してとりあえず動作させる。
メッセージボックスはブロック命令でないことは今までの実験から分かっていたのだが
まず、app_cnt1を5ぐらいでメッセージボックスが複数出ることを確認した。
出来の悪いjavascriptでメッセージボックスがたくさん出るのと似ている。

次にsleep命令で10秒コールバックを止めた。
コールバックはそもそも別スレッドなのでアプリの体裁としてはなんら障害は見られない。
そうするとタイマー間隔が5秒の場合15秒等間隔でコールバックが実行された。

つまりタイマーはつどつどスレッドを生成するわけではないし
タイマーリクエストも溜め込まれるわけではなくて随時実行するということだと思われる。
これは音楽演奏などのときは「もたれ」という感じになるのだろうか?
遅れた分まとめてリクエストがくるならば15秒間隔にはならず
ブロックされたあとは10秒間隔になったはずだ。

一方でメッセージボックスで待機して関数が完了していない場合は
次の命令が呼び出されている。
完全に自前の関数だけでやっている場合はともかく
APIを使っている場合はネスティングして呼び出される可能性はあるということだと思う。
また全く違うコールバックを用意しておいてマルチメディアタイマーを連続して呼び出したところ
sleepを入れた場合に限って
一つ目のタイマー処理の回数分が終わってから次のタイマー処理が始まった。
スケジュールが狂うと挙動がおかしくなるようだ。この辺のロジックがまだ明確でない。

これらのことからタイマーを使った定間隔処理は予約した時間で発生するものではないから
コールバックでの時間測定が必要で
コールバックはWindowProc同様APIの途中から再呼び出しされる可能性もあるので
最呼び出しに対するフラグやカウンター処理は抜けないといえそうだ。
そう考えると結局タイマーは繰り返し呼び出しするのではなくて
処理過程で次を呼び出す形にするのが良いと思えた。

ちなみに今回コールバックからUIスレッドへコマンドをポストした。

postmessage(App_Window,$111,dwUser,0);//WM_COMMAND

のようなコードだ。
遅延動作させるのによい。

メニューを一応対応させているが無関係にいろいろコマンドを用意しておくのも手だ。
複数のイベントオブジェトをまとめて操作するために同期目的でWaitableTimerを使うというのも一つの考え方だが
1世代古い命令であるマルチメディアタイマーと比べて開始時間の精度が高い以外メリットが見出せなかった。
スレッドタイマーもsleepかなんか使ったものの方が手っ取り早い。

UIスレッドのループチェックでスケジュールをリスト型管理を自前でする前提だと
WM_TIMERは全くメリットがないと思ったが
場合によっては適当にぶったぎって動作してくれると楽だという見方もあるし
IDで管理できる分timeSetEventよりこちらの思惑どうりに動いてくれそうに感じた。
また現在メインループでFPS制御する体裁だが
こちらはWaitmessageで留まって別スレッドでタイマーで呼び出す方法も考えたいところだが
基本要素は分かったので先に進むことにする。

ちなみに時間的要因としてTimeサーバーへのアクセスとかユリウス通日等のルーチンは組む予定。


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

メールアドレス:

ホームページアドレス:

コメント:

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


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

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