timer イベントに関する質問

解決


nasu  2008-05-14 13:41:29  No: 139712  IP: 192.*.*.*

一分ごとにタイマーイベントを発生させたいと考えています。

timer1.Interval = 60000  

Private Sub timer1_Timer()

処理1

labe1.caption=now()

処理2

end sub

において何度かイベントが起こっていると時間がずれてきます。
イベント内でTimer1.Enable=Falseはしていません。  
タイマーイベント内の処理が終了するまで次のインターバルは開始されないのでしょうか?

編集 削除
特攻隊長まるるう  2008-05-14 13:56:50  No: 139713  IP: 192.*.*.*

Timer の精度が悪いのは有名な話です。
キーワード『Timer 正確  VB』などでぐーぐる検索してみてください。

編集 削除
 2008-05-14 16:29:17  No: 139714  IP: 192.*.*.*

Timerのズレに関しては、すでに書かれてるので置いといて、
余談になりますが、Timerイベントの処理が終了しなくてもTimerは止めない限り
OS任せで(?)動きっぱなしで、次のイベントは時間が来れば発生するはずです

とは言え、Timerイベントプロシージャ内でDoEvents書いたりしてOSに処理が
戻らない限り、実際に処理は走らない(待ち状態になる)ような気もしますが…

時間計測などを行う処理でなく、Timerイベントプロシージャの処理が
時間が掛かるものであれば、処理の最初でTimerを止め、処理終了時に
Timerを再起動するのが良いかもしれません
(あくまで余談ですので、訳わかんなければ放置してください)

編集 削除
nasu  2008-05-15 10:15:39  No: 139715  IP: 192.*.*.*

特攻隊長まるるうさん、あさん  ありがとうございます。

おっしゃるとおり調べてみましたらTimerの誤差の原因あるみたいですね。

しかしながら、その誤差より更に大きい気がして
timerイベント内でsleep(10000)をしてみました。
すると次のtimerイベントまでに70秒と10秒遅れます。
やはりtimerイベント終了後から60秒とっているような・・・。

編集 削除
通りすがり  2008-05-15 12:41:42  No: 139716  IP: 192.*.*.*

チェックボックスとテキストボックス「0をいれておく」でどうでしょう?
マルチ処理は出来なく、プロセジャ実行中にプロセジャ実行はできないかと。。。解決?

Private Sub Timer1_Timer()

Form1.Text1.Text = CStr(CLng(Form1.Text1.Text) + 1)

Do
    DoEvents
    If Check1.Value = 1 Then
        Exit Do
    End If
Loop

End Sub

編集 削除
 2008-05-15 16:25:00  No: 139717  IP: 192.*.*.*

ふむ…そうなるなら、「Timerイベントプロシージャ処理中はTimerが待つ」または
「Timerの管理はあくまでアプリ内で行う(OS任せではない)仕様で、Sleep APIは
スレッドを完全に停止させるから+10秒掛かった」かの、どちらかかもしれない
私の認識が足りなかったようで…失礼
ただ、これだけでは、Timerが待ってくれるのかどうかは確定ではない気がする
…知ってる方、補足願いたい
(Sleepでスレッドが完全に停止するのは確実)

ちなみに通りすがり氏の内容は、「処理中フラグ」を作って制御している…
みたいな方法、なのだろうか?
確かに、その手のフラグで逃げたことはあるが(Timerでは無かった気が…)

編集 削除
 2008-05-15 17:58:31  No: 139718  IP: 192.*.*.*

Interval プロパティーが 60000 に設定された Timer1 の Timer1_Timerイベントは、
60秒に置きに発生するのではなく、前回の Timer1_Timerイベント が終了してから 60秒経ったら発生します。
よって、Timer1_Timerイベントが発生してイベントが終わるまでに20秒かかる場合は、80秒置きに発生するということになります。

また、Timer1_Timerイベントのコードの中に DoEvents を入れていたとしても、
Timer1_Timerイベント実行中にまた Timer1_Timerイベント が発生してしまうということもありません。
Timer1_Timerイベントが終わるまで待っています。

一分ごとにタイマーイベントを発生させたいときは、
Timer1_Timerイベントのコードの最初と最後に時刻を測定して Timer1_Timerイベント にかかった時間を計って、その時間を60秒から差し引いた値をInterval プロパティーにセットすればうまくと思います。

Timer1.Interval = 60000  

Private Sub timer1_Timer()

    Start = Timer

    処理1

    labe1.caption=now()

    処理2

    Finish = Timer

    TotalTime = Finish - Start  
        
    Timer1.Interval = 60000 - TotalTime * 1000

end sub

編集 削除
 2008-05-15 20:00:28  No: 139719  IP: 192.*.*.*

補足)
上の方法でもほんの少しずつですが、ずれが出てきます。(ずれが蓄積していく)。
Timer関数(1秒単位)ではなく API の GetTickCount(1m秒単位) を使えばずれは少なくなります。

時報のように正確に(ずれが蓄積しないように)一分おきに発生させたいときは、経過時間ではなく時刻を取得してInterval プロパティーを調節してやる必要があります。
また、場合によっては、 Timer1_Timerイベントに60秒以上かかってしまったときの対策も必要となります。


下の例は、Timer1_Timerイベントに60秒以上かかった場合の対策をしていない場合のものです。

Dim mTime As Date 'Timerイベントの実行予定時刻が入るモジュール変数。

Private Sub Command1_Click()
'スタート

    Timer1.Enabled = False
    mTime = DateAdd("n", 1, Now) '予定時刻をセット。
    Timer1.Interval = 60000
    Timer1.Enabled = True

End Sub

Private Sub timer1_Timer()

    処理1

    labe1.Caption = Now()

    処理2

    mTime = DateAdd("n", 1, mTime) '一分後に予定。
    Timer1.Interval = DateDiff("s", Now, mTime) * 1000 '予定とのずれを調節する。
    
End Sub

編集 削除
nasu  2008-05-16 10:17:35  No: 139720  IP: 192.*.*.*

現状ではタイマーイベント内の処理で60秒以上かかることがないので
Qさまのプログラムを参考にさせていただきなんとかタイマーの誤差を
補正することができました。
ご意見ご指導くだされた皆様本当にありがとうございました。

編集 削除