プログラミングテクニック

ホーム   アプリケーション掲示板  

情報が役に立ったら、ぜひ、をクリックして、ポイントを贈ってください。

Windows
  話題更新日時   概要
 MySQLのインストール   2004/03/12 Windows2000にMySQLをインストールする

Visual Basic
  話題更新日時   概要
 設定データをファイルに保存する方法   2004/03/18 設定データをファイルに保存する方法
 MySQLを操作する   2004/03/12 MySQLを操作する方法
 安全な一定の時間待つプログラム   2003/12/16 よく紹介されているコードには問題があります。

C++
  話題更新日時   概要
 C++のメモ書き   2004/04/22 C++のメモ書き
 ATL/WTLのメモ書き   2004/08/03 ATL/WTLのメモ書き

安全な一定時間待つプログラム

 例えば、以下のVisual Basicの関数では、指定の時間、待って制御を返します。

Sub Sleep(ByVal WaitTime)
  Dim Start As Single
    
  Start = Timer
  Do While Timer < Start + WaitTime
    DoEvents
  Loop
End Sub

 よく見かけるプログラム(コード)ですが、このプログラムには問題があります。
Timer関数の解説では、「午前 0 時 (真夜中) から経過した秒数を表す単精度浮動小数点数型 (Single) の値を返します」とあります。つまり、午前0時に、ゼロ(0)に戻るわけです。
上記のプログラムを、午前0時の直前に実行すると、次の日まで待つことになったり、最悪の場合、無限ループに入る可能性があるのです。
 次が時間カウンタが一定時間後に0になることに対応した改訂版です。

Sub Sleep2(ByVal WaitTime)
  Dim Start As Single, n As Single
  Const m = 60# * 60# * 24#
  
  Start = Timer
  Do While Timer < Start + WaitTime
    If Start > Timer Then
      If Timer > WaitTime - (m - Start) - 1 Then
        Exit Do
      End If
    End If
    DoEvents
  Loop
End Sub

 スマートな記述ではありませんが、現在の時間が開始時間より小さい場合を考慮しています。
If Timer > WaitTime - (m - Start) - 1 Then
の「-1」は、Timerの取り得る値が、1日の秒数より1少ないことを意味します。

 次に気をつけることは、変数の範囲(型の範囲)です。Sleep2のプログラムでは問題は発生しませんが、例えば、Singleを、Integerにすると、午前0時に実行すると正常なのに、12時に実行するとエラー(オーバーフロー)が発生します。Visual Basicでは暗黙のうちに適切な型に変換してくれると、安心していると、この問題に、よく出会います。バリアント型を使えば良いと思われるかもしれませんが、この型を使うと予想外の変換を行ってしまう場合があるので、私は使用していません。
 変数の範囲(型の範囲)の問題に関しては、使用する変数の型を、ひとつ大きいものにするのが簡単です。例えば、単精度浮動小数点数型(Single)なら倍精度浮動小数点数型(Double)、長整数型(Long)なら通貨型(Currency)を利用すると良いでしょう。

 最後に注意が必要なのは、ループ内でDoEventsを使っていることです。
 「DoEvents」は、コード(プログラム)の実行中でも、メニューボタンの操作などを行えるようにできる便利な関数ですが、逆に言えば、実行中にも関わらず、他の操作が出来てしまうということです。
 例えば、コードの実行中に、フォームを閉じることができますが、コードの実行は継続されています。見た目では、作成したアプリケーションが終了しているのに、実際には、バックグランドで動いているという現象が発生することがあるのです。
 プログラム終了時にプログラムが終了しないことに対する対策を行ったのが次のプログラムです。

Dim Flg

Sub Sleep3(ByVal WaitTime)
  Dim Start As Single, n As Single
  Const m = 60# * 60# * 24#
  
  Start = Timer
  Do While Timer < Start + WaitTime
    If Start > Timer Then
      If Timer > WaitTime - (m - Start) - 1 Then
        Exit Do
      End If
    End If
    If Flg = False Then
      Exit Do
    End If
    DoEvents
  Loop
End Sub

Private Sub Form_Load()
  Flg = True
End Sub

Private Sub Form_Unload(Cancel As Integer)
  Flg = False
End Sub

 フォームがロードされるときに、グローバル変数FlgをTrueに設定し、アンロード時にFalseに設定するようにしています。Sleep3内では、FlgがFalseの場合は、すぐにループを抜けるようにしています。

 ここまで、考慮すれば、かなり安全なプログラムになると思います。

ZENのシステムを利用しています