[VB6][VBA][VB.net] 再談 Timer 控制項 - 時間解析度過低的解決方法(二)用 Do…Loop 模擬 Timer

[VB6][VBA][VB.net] 再談 Timer 控制項 - 時間解析度過低的解決方法(二)用 Do…Loop 模擬 Timer

  1. 前文說到 VB6 提升 Timer 解析度的方法在 VB.net 可以用 Stopwatch 解決。
  2. 現在就來實作一個測試程式來觀察比較一下。
  3. 環境 OS 是 Windows7 x64,CPU 2.6GHz。

  • 程式使用三種計時的基準:
    1. 使用 System.Windows.Forms.Timer 類別(原來工具箱的控制項)
    2. 使用 Microsoft.VisualBasic.DateAndTime.Timer() 函數
    3. 使用 System.Diagnostics.Stopwatch 類別
  • 結果如下:
    1. 計時 0.5 秒, Interval = 1ms(Count 應為 499) 時只有 Stopwatch 接近正常,Timer Control 清楚的表明在半秒之內只做 32 次(前文所說的 1/64)。
    2. 計時 0.5 秒, Interval = 2ms(Count 應為 249) 時只有 Stopwatch 和 Timer() 函數正常。Timer Control 仍堅持只做 32 次(0.5*64 = 32)
    3. 計時 1.0 秒, Interval = 2ms(Count 應為 499) 時只有 Stopwatch 和 Timer() 函數正常。Timer Control 做了 64次(1*64 = 64)
      1 2 3

      image

      image

      image

  • 再看其他條件下的表現:
    1. 計時 0.005 秒,Interval = 1ms(Count 應為 4) 時只有 Stopwatch 最正確,Timer() 函數因為 Time up 時間點不夠精準多算了一次。
    2. 計時 0.1 秒,Interval = 5ms(Count 應為 19) 時只有 Stopwatch 最正確,Timer() 函數仍多算了一次,Timer Control 六次(0.1*64 = 6.4)。
    3. 計時 2 秒,Interval = 20ms(Count 應為 99) 時只有 Stopwatch 最正確,Timer() 函數仍多算了一次,Timer Control 做 64 次(Interval 太小所致)。
      1 2 3

      image

      image

      image

  • 看完以上測試結果,會不會覺得做成獨立的控制項,再封裝成 dll 會更方便呢?下回再貼囉。

 


  • 原始碼如下:
    
    Imports System.Diagnostics
    Imports Microsoft.VisualBasic
    Imports System.Text
    Public Class Form1
        Dim 計時元件 As New Stopwatch
        Dim 時訊週期 As Long = Stopwatch.Frequency
        Dim 經過時間 As Single = 0
        Dim 總計時秒數 As Double
        Dim 事件週期 As Double
        Dim 事件觸發次數 As Long = 0
        Dim 結果列表 As New List(Of String)
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            cbx_測試秒數.SelectedIndex = 5
            cbx_事件週期.SelectedIndex = 4
            Text = "Timer 精確度測試"
            計時元件.Reset()
            cbx_Change()
        End Sub
        Private Sub cbx_Change() Handles cbx_測試秒數.SelectedIndexChanged, cbx_事件週期.SelectedIndexChanged
            Try
                If cbx_測試秒數.Text <> "" Then 總計時秒數 = (cbx_測試秒數.Text)
                If cbx_事件週期.Text <> "" Then 事件週期 = (cbx_事件週期.Text)
                lbl_CPU_clock.Text = "CPU Clock = " & Format(時訊週期 * 1024 / 1000000000, "###.### GHz")
                lbl_描述.Text = "在 " & 總計時秒數 & "s 內每隔 " & 事件週期 * 1000 & "ms 觸發一次事件。"
            Catch ex As Exception
            End Try
        End Sub
        Sub 重建測試條件()
            事件觸發次數 = 0
            經過時間 = 0
            結果列表.Clear()
            lst_顯示結果.Items.Clear()
        End Sub
        Sub 顯示結果(ByVal btn As Button)
            lst_顯示結果.ForeColor = btn.ForeColor
            For Each i In 結果列表
                lst_顯示結果.Items.Add(i)
            Next
        End Sub
        Private Sub B1() Handles btn_Timer控制項.Click
            重建測試條件()
            Dim 測試開始時間 As Long = 0
            Dim 階段計時起點 As Double = 0
            Dim 比對次數 As Long = 0
            計時元件.Start()
            測試開始時間 = 計時元件.ElapsedTicks
            階段計時起點 = 計時元件.ElapsedTicks
            Timer1.Interval = 事件週期 * 1000
            Timer1.Enabled = True
            Do
                Application.DoEvents()
            Loop While (計時元件.ElapsedTicks - 測試開始時間) / 時訊週期 < 總計時秒數
            Timer1.Enabled = False
            顯示結果(btn_Timer控制項)
        End Sub
        Private Sub B2() Handles btn_Timer函式.Click
            重建測試條件()
            Dim 測試開始時間 As Long = 0
            Dim 階段計時起點 As Double = 0
            Dim 比對次數 As Long = 0
            計時元件.Start()
            測試開始時間 = 計時元件.ElapsedTicks
            階段計時起點 = Microsoft.VisualBasic.Timer
            Do
                比對次數 += 1
                '---用 Timer() 函數值判判是否執行計時器事件----
                經過時間 = Microsoft.VisualBasic.Timer - 階段計時起點
                If (Microsoft.VisualBasic.Timer - 階段計時起點) > 事件週期 Then
                    階段計時起點 = Microsoft.VisualBasic.Timer
                    事件觸發次數 += 1
                    '-----計時器事件放在這裡--------------------------------------------------------------
                    結果列表.Insert(0, "第 " & 事件觸發次數 & " 次事件" & vbTab & 比對次數 & vbTab & 經過時間 & " 秒")
                    '-------------------------------------------------------------------------------------
                    比對次數 = 0
                End If
                Application.DoEvents()
            Loop While (計時元件.ElapsedTicks - 測試開始時間) / 時訊週期 < 總計時秒數
            顯示結果(btn_Timer函式)
        End Sub
        Private Sub B3() Handles btn_StopWatch.Click
            重建測試條件()
            Dim 測試開始時間 As Long = 0
            Dim 階段計時起點 As Long = 0
            Dim 比對次數 As Long = 0
            計時元件.Restart()
            測試開始時間 = 計時元件.ElapsedTicks
            階段計時起點 = 計時元件.ElapsedTicks
            Do
                比對次數 += 1
                '---用 Ticks Count 判判是否執行計時器事件----
                經過時間 = (計時元件.ElapsedTicks - 階段計時起點) / 時訊週期
                If 經過時間 > (事件週期) Then
                    階段計時起點 = 計時元件.ElapsedTicks
                    事件觸發次數 += 1
                    '-----計時器事件放在這裡--------------------------------------------------------------
                    結果列表.Insert(0, "第 " & 事件觸發次數 & " 次事件" & vbTab & 比對次數 & vbTab & 經過時間 & " 秒")
                    '-------------------------------------------------------------------------------------
                    比對次數 = 0
                End If
                Application.DoEvents()
            Loop While (計時元件.ElapsedTicks - 測試開始時間) / 時訊週期 < 總計時秒數
            顯示結果(btn_StopWatch)
        End Sub
        Private Sub T1() Handles Timer1.Tick
            Static 階段計時起點 As Long = 0
            事件觸發次數 += 1
            經過時間 = (計時元件.ElapsedTicks - 階段計時起點) / 時訊週期
            結果列表.Insert(0, "第 " & 事件觸發次數 & " 次事件" & vbTab & "idle" & vbTab & 經過時間 & " 秒")
            階段計時起點 = 計時元件.ElapsedTicks
        End Sub
    End Class
    

 


專案包下載 timer_demo.rar


ku3