2008年07月14日

時間計測とタイマー その5 結構使いにくいWaitableTimer

本命視していたWaitableTimerの実験をしてみた。案外と使いにくい。
それでもWindows98SEでもちゃんと動作した。
MSDNでは「Win98サポートせず」となっておりWebでも資料は少ない事に納得がいった。
ようするにテストされていない。使われていない関数なのだ。
スレッドを複数組んだ場合には使っても良いだろうが
timeSetEventとtimegettimeの組み合わせが良いという結論に至った。
機器制御などにはよいと思う。※呼び出し間隔がmsオーダーなのでだめだとわかった。

今回はWindowのフリーズ回避にtimeSetEventのコールバックによるスレッド生成を利用した。
この関数をアプリ内で使う場合
CreateWaitableTimerでタイマーをオブジェクトを生成

SetWaitableTimerでタイマーをセット、待機

タイマーAPCと呼ばれるコールバックで実行

CancelWaitableTimerで繰り返しているなら停止

CloseHandleでタイマーオブジェクトの削除

の流れとなる。

例えば


var

WTimer1 :THandle;
WTimer1t:TLargeInteger;
Wtcnt1:LongInt;
onwmtimeout:Pointer;

とすると

アプリケーションのイニシャライズで

WTimer1:=CreateWaitableTimer(nil,false,nil);


終了処理で

CancelWaitableTimer(WTimer1);
CloseHandle(WTimer1);

とすればいいのがわかる。

呼び出しは一回だけの場合は


WTimer1t:=-50000000;
SetWaitableTimer(WTimer1, WTimer1t,0, onwmtimeout, Pointer(l_param), TRUE);

というところだろうか。
WTimer1tはタイマーの実行される時間なのだが
負の場合は呼出し後の時間(100ns単位)で
正の場合は1601/01/01 00:00:00 からののべ時間
これは分かりにくいかもしれないけれど
FileTimeToSystemTimeやその逆のSystemTimeToFileTimeを用いることで現時間などから取得は可能だ。

タイマーAPCは


Procedure WMTimeCallBack1 (
param:LongWord;TimerLow,TimerHigh:LongWord)stdcall;

の形式で

onwmtimeout:=@WMTimeCallBack1

等として入れ替えればよい。

この関数はFileTime値も得られるのだが
これを他のタイマー関数の感覚で呼び出してもコールバックは呼ばれない。
待っていないといけないのだ。
またWindows98で動くもののFileTime値が得られない。
まとめると


  1. 呼び出し元はSleepEXやWaitForSingleObjectEx等で待機しなくてはいけない。

  2. Windows98SEでも動作する。

  3. Windows98SEのコールバックではFileTimeの値が0になる。



ということのようだ。

基本的にこのタイマーはマルチスレッドを前提としているのだが
一番痛いのはSetWaitableTimerを呼び出した後別スレッドでコールバックが実行されるのはいいとして
呼び出しスレッドが待機状態で無いといけないという点にある。
命令が待ち必須ならばスレッド生成とセットの関数にすればよいのだがその辺がだめなのだ。

それはまた一般的なクライアントの場合
UIスレッドから直接呼び出すとフリーズしてしまう事を意味する。
定期的に動くものならまだしも柔軟なタイミングで動作するものの場合は
かなりコーディングが煩雑になるわけで
できるだけスレッドを減らしたいならばこのましくない。
とするとむしろコンソールアプリケーションでタイミングを取りたい場合に使うならば
コーディングのロスが少ないのではないかと思われる。

現時点では本格的にマルチスレッドには取り組んでいないので動作時は

SleepEx(10000,true);

をSetWaitableTimerの直後においた。

このままだと呼び出した後Windowがフリーズしてしまう。
そこでtimeSetEventのコールバックでSetWaitableTimerを呼び出した。
そうすれば割と簡単に別スレッドで待機状態になりタイマーが使える。

SetWaitableTimerの最終パラメータをTRUEにするとサスペンドやハイパネーションから復帰するそうだが
うまくいかなかった。
これは現在テストしているPCのせいかもしれない。

SetWaitableTimerの最高精度の100nsはまぁ出ないだろうが
1ms以下で刻めるので電子工作みたいなジャンルの模型制御には使えるとは思うが
汎用アプリケーションではtimeSetEventを使えば充分だと思われる。

timeSetEventのコールバックで命令を出す発想自体が非マルチスレッドプログラミングの発想であり
矛盾がある。

Web上でも実用的な話ではなくこの程度の「動作実験サンプル」程度のものしかないのも
使いにくさゆえだろう。

このテストのためにWin98機を組み立てたようなものなのだが得られたものは少ない。
それでもMSDNの内容が明示的に間違っているという事を示す例としてはこの記事にはそれなりの意義はあるかもしれない。

但し休止、ないしはサスペンドからの復帰はタイマーアプリケーション用などに確保したいインフラではあるので
いずれ解決したい。
posted by Xo_ox at 21:00| Comment(1) | Win出直し | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
これに限らないですがAPCは使いにくいですよね。Waitable TimerもAPCを使わないのが楽です。SetWaitableTimerの4番目の引数(コールバック関数)にヌルを渡せば、APC無しで使えます。この場合、Waitable TimerのハンドルをWaitForSingleObjectに渡して、時間が来るまで待機という使い方になります。

時間が来たらコールバック関数を呼ぶという使い方をしたければ、スレッドを1つ立てて、そこでWaitForSingleObjectを呼び、コールバック関数を呼ぶという処理を自分で書くといいです。ただし、Windows 98非対応でよければRegisterWaitForSingleObjectが同じことを実装しています。
Posted by egtra at 2010年11月06日 18:32
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

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


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

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