QA@IT
この質問・回答は、@ITの旧掲示板からインポートされたものです。

VBでのインターバルタイマ

短時間でのインターバルタイマがうまく動作しなくて困っています。
10ミリ秒でのインターバルタイマを実現したいのですが、Windowsマシンによって
挙動がマチマチです。
あるマシン(VAIO Pen2,300MHz)では10ミリ秒でタイマがかかります。最新マシン(DELL Pen4,3GHZ)では10ミリ秒に設定しても15ミリでしかタイマがかかりません。
そこそこの精度(1ミリ秒以内)かつCPU低負荷で、任意のマシンでインターバルタイマを動作させる方法についてご教授ください。

Timer1.interval=10
Timer1.Enabled=False

質問者:Nakasaitama

回答

Nakasaitamaさんの書き込み (2004-02-03 00:57) より:

Timer1.interval=10

Timer1.Enabled=False

 MSDNに記述がありますが、Timerは2つあります。片方は精度が低いです。どちらを使っていますか?

投稿者:Jitta

編集 履歴 (0)

VBのタイマーで10ミリ秒の精度って出せます?
(純粋にぼくの疑問なだけですが・・・^^;
こういうのって、メッセージキューにメッセージがたまってたりすると
実際のタイマーイベントが処理されるタイミングはマチマチになって
しまうと思うのですが。
精度を出したい場合はAPIのマルチメディアタイマーを使うのが一番だと
思います。(VBでこのAPI使えるかどうか知りませんが^^;

--追加
Jittaさんへ、

>MSDNに記述がありますが、Timerは2つあります。片方は精度が低いです。どちらを使っていますか?

2つって見つからなかったんですが、タイマーコントロール以外にあるのですか?

タイマーコントロールは、

システムは、1 秒に 18 のクロック信号を発生させます。したがって、Interval プロパティはミリ秒単位で指定されますが、実際の時間間隔の精度は、せいぜい 18 分の 1 秒です。

だそうですね^^;

他にあるのであれば、ぜひ知りたいです。
[ メッセージ編集済み 編集者: りばぁ 編集日時 2004-02-03 10:11 ]

投稿者:りばぁ

編集 履歴 (0)

りばぁさんの書き込み (2004-02-03 10:03) より:

VBのタイマーで10ミリ秒の精度って出せます?

精度を出したい場合はAPIのマルチメディアタイマーを使うのが一番だと

思います。(VBでこのAPI使えるかどうか知りませんが^^;

 えっとですね、「2種類」あるんですね。名前空間で言うと、
System.Threading.Timer
System.Timers.Timer
System.Windows.Forms.Timer … 55ミリ秒の精度
う〜ん、今MSDNを見ると、3種類でした。このうち、Windows.Forms.Timerは、55ミリ秒の精度に制限されています。ツールボックスには、おそらくこのTimerが配置されているんですね。そのため、ツールボックスにあるTimerを使うと、55ミリ秒以上の精度、まぁ秒単位ですね、でしか、使用できません。
 ミリ秒単位が必要な場合は、System.Timers.Timerを使ってください。

追記:
 あうあう。VB.NETです。VB6.0は、精度悪いのですよね、聞いた話。

さらに追記:
 ん???VB.NET?VB6.0?どっち?
[ メッセージ編集済み 編集者: Jitta 編集日時 2004-02-03 10:21 ]

投稿者:Jitta

編集 履歴 (0)

こんにちわ。

Jittaさんの書き込み (2004-02-03 10:16) より:

追記:

 あうあう。VB.NETです。VB6.0は、精度悪いのですよね、聞いた話。

さらに追記:

 ん???VB.NET?VB6.0?どっち?

なるほどw .NETの話しでしたか。

スレ主さんのコードから見ると、VB6.0のタイマーコントロールのように見えたもので、
こちらで勝手に判断してしまいましたが^^;

環境を書いて頂かないと、混乱のもとになると言うことが良く分かる一件ですね

投稿者:りばぁ

編集 履歴 (0)

(りばぁさん 投稿日時: 2004-02-03 10:29)

スレ主さんのコードから見ると、VB6.0のタイマーコントロールのように見えたもので、

こちらで勝手に判断してしまいましたが^^;

環境を書いて頂かないと、混乱のもとになると言うことが良く分かる一件ですね

いや(^^;、ここは.NETの会議室なんですから、デフォルトは.NETでしょう。
もしVB6の質問であれば単なる板違いということで・・・。

最初の投稿にある

Timer1.Interval=10
Timer1.Enabled=False

このコードはここだけを見れば、VB6のTimerコントロールの場合でも、
VB.NETでSystem.Windows.Forms.Timerクラスを使っても、
System.Timers.Timerクラスを使っても同じコードになります。

(Jittaさん 投稿日時: 2004-02-03 10:16)

追記:

 あうあう。VB.NETです。VB6.0は、精度悪いのですよね、聞いた話。

VB6までのTimerコントロールはSystem.Windows.Forms.Timerと同じで、
55ms程度の精度です。(でも、NT系では10msぐらいの精度が出たりします)

投稿者:よねKEN

編集 履歴 (0)

System.Timers.Timer についてちょっと簡単な実験をしたんですけど、どうもElapsedEventArgs.SignalTime の精度がおかしいみたいです。以下、あくまでうちの環境の場合です。

Timer.Interval を 1 に設定した場合、この値を列挙させてみると 15[ms] 置きの値になります。つまり「複数回同じ値が設定されて発生」します。不思議に思ったので timeGetTime() を利用して計測してみると、きちんと 1-2[ms] 置きの値が計測できます。

この辺から考えて「System.Timers.Timer.Elapsed はきちんとした精度で発生するが、それに渡される ElapsedEventArgs.SignalTime の値は精度が 10-20[ms] ぐらいしかない」ようです。

それで ElapsedEventArgs.SignalTime は System.DateTime 型なので、この辺が原因ではないかと予想して、System.DateTime の挙動を少し調べてみたのですが、DateTime.Now を連続で呼び出したときの挙動がこれとそっくりです(Interval=0がずっと続き階段状に約15ms間隔で上昇していく)。この辺が原因ではないかなぁ、と予想しています。

というわけで、
・System.Timers.Timer の発生精度は問題ない(1[ms]まで)。
・ただそれを確認するためにはtimeGetTime, QueryPerformanceCounter等使わないとだめ(DateTimeの精度は悪い?)。
という感じかな、と現状判断してます。

※あくまで、限定的な実験による推測です。それが求められる場合きちんとした実験を自分でするべきです。

投稿者:ya

編集 履歴 (0)

大変申し訳ありません。
VB6.0&Windows2000で、使用したのはツールボックス78のタイマです。
色々と投稿いただき、様子が大体わかりました。当方の責任にて検証してみます。

ところで、ここからオカルトなのですが。
簡単に複数PCで確認したところ、デバイスがシステムに割り付けられているIRQの最大値が15のPCでは10msecインターバルタイマがかかります。ところがIRQが15を超えて割り付けられているPCでは10msecはかからず、15msec程度になります。どうもPC依存の可能性が大なようです。もともと55msecがせいぜいとのお話なので、10msecが実行されるのはオマケかも知れませんが。(IRQは0-15と思っていたのですが、16以上もあるのですね)

投稿者:Nakasaitama

編集 履歴 (0)

こんにちわ。
ええっと、IRQとの関係は良く分かりませんが^^;
PCによって変わる可能性は否定出来ないと思います。

タイマーコントロールでのイベントは、設定した間隔ごとに
メッセージを飛ばしているわけで、
メッセージループは基本的にウィンドウに1つ持っている
だけなので(インターフェーススレッドの考え方です。
端折ります^^;)メッセージキューにメッセージが
たまっている状態では、タイマーイベントがいつ入るかは
不定です。
なので、精度が必要な場合は向かないと思います。
先のぼくのレスでAPIのマルチメディアタイマーを挙げたのは、
これは独自スレッドを起動しているので、上記の問題は発生
しない(はず)です。(自信なさげですが^^;)
。。が、先のレスでも言いましたが、VBで出来るかは分かりません^^;

長くなってしまいますし、今回の場合はタイマー自体の性能の問題なので
適用できないかもしれませんが、精度が必要な場合の実現方法の例でも、、、

まず、タイマーコントロールのインターバルは必要な間隔よりもずっと短くします。
(ここで、すでに今回の場合はダメというわけですが^^;)
そのイベント内でtimeGetTimeなどで時刻を取得し、前回時刻との比較により
時間経過をチェックし、必要な時間が経過した時に処理を行う。
という手順でどうでしょうか?
まぁ、これできっちり精度が出る訳ではありませんが^^;

投稿者:りばぁ

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。