如果要求线程按照规定的时间间隔周期性地执行的话,可以使用Timer类将需要调用的方法加入队列等待周期性执行。Timer类包含在System.Threading命名空间。Timer类通过系统提供的线程池线程在每次计时器期满时运行计时器委托。因为System.Threading.Timer对象是在单独的线程中执行,因此在退出应用程序之前需要完全终止计时器。
.NET Compact Framework中的其他Timer类,比如System.Windows.Forms命名空间的Timer类与System.Threading命名空间的Timer类有所不同。System.Windows.Forms.Timer类每次计时器期满时产生WM_TIMER系统消息来触发事件,不能使用单独的线程来执行。System.Windows.Forms.Timer类使用简单且适合用于周期性的更新用户界面。如果需要高精度的计时器,则应该使用System.Threading.Timer。
图23-3展示了使用不同Timer类的精确程度比较。在如图23-3所示的应用程序中分别创建了System.Threading.Timer和System.Windows.Forms.Timer实例,并设置时间间隔为100毫秒。
图23-3:不同Timer类的精确性比较
这个TimerDemo应用程序测量不同Timer类的平均时间间隔。当点击“模拟界面操作”按钮时,应用程序产生一个额外的处理活动,使不同Timer类在行为上的差别更加清晰明显。Windows.Forms.Timer对象从消息队列接收到一个WM_TIMER消息时调用它自己的委托。Windows.Forms.Timer是单线程组件,精度限定为55毫秒。当应用程序处理繁忙时,时间间隔小于55毫秒的WM_TIMER消息会被忽略。换句话说,由于Windows.Forms.Timer的精度限制,消息队列中不能存在超过一个未处理的WM_TIMER消息,否则多余的WM_TIMER消息会重叠合并,因而丢失计时信号。而在图23-3中正确的做法是,Threading.Timer对象运行在一个单独的线程中,计时器的间隔时间一到则随时被运行。Threading.Timer对象负责处理精确度(两个Timer类的处理次数),因为“模拟界面操作”按钮被点击后,主线程的优先级被临时降低,以此模拟应用程序处于繁忙状态。清单23-7说明了TimerDemo应用程序的实现代码。
清单23-7:TimerDemo应用程序实现代码
Public Class TimerDemo
Private threadingTimer As Threading.Timer
Private prevTimerTicks As Long
Private avgTimerTicks As Long
Private numTimerExpirations As Long
Private timerRunning As Boolean = False
Private Sub btnThreadingTimer_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnThreadingTimer.Click
If timerRunning = True Then
timerRunning = False
While Not threadingTimer Is Nothing
Threading.Thread.Sleep(0)
End While
avgTimerTicks = avgTimerTicks / numTimerExpirations
lThreadingTimer.Text = "Avg. Timer Interval: " + avgTimerTicks.ToString() + _
" ms (" + numTimerExpirations.ToString() + ")"
btnThreadingTimer.Text = "Start Threading Timer"
btnFormsTimer.Enabled = True
Else
timerRunning = True
lThreadingTimer.Text = "Timer running"
btnThreadingTimer.Text = "Stop Threading Timer"
btnFormsTimer.Enabled = False
avgTimerTicks = 0
numTimerExpirations = 0
prevTimerTicks = Environment.TickCount
threadingTimer = New Threading.Timer(New _
Threading.TimerCallback(AddressOf ThreadingTimerTick), Nothing, 0, 100)
End If
End Sub
Private Sub btnFormsTimer_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnFormsTimer.Click
If timerRunning = True Then
timerRunning = False
avgTimerTicks = avgTimerTicks / numTimerExpirations
lFormsTimer.Text = "Avg. Timer Interval: " + avgTimerTicks.ToString() + _
" ms (" + numTimerExpirations.ToString() + ")"
btnFormsTimer.Text = "Start Forms Timer"
btnThreadingTimer.Enabled = True
Else
timerRunning = True
lFormsTimer.Text = "Timer running"
btnFormsTimer.Text = "Stop Forms Timer"
btnThreadingTimer.Enabled = False
avgTimerTicks = 0
numTimerExpirations = 0
prevTimerTicks = Environment.TickCount
formsTimer.Interval = 100 ' 利用窗体上放置的Timer控件
formsTimer.Enabled = True
End If
End Sub
Private Sub btnProcessing_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnProcessing.Click
btnProcessing.Enabled = False
Threading.Thread.CurrentThread.Priority = Threading.ThreadPriority.BelowNormal
For i As Long = 0 To 5000000
Next
btnProcessing.Enabled = True
Threading.Thread.CurrentThread.Priority = Threading.ThreadPriority.Normal
End Sub
Private Sub ThreadingTimerTick(ByVal state As Object)
If timerRunning = False Then
threadingTimer.Dispose()
threadingTimer = Nothing
Else
Dim currentTimerTicks As Long = Environment.TickCount
avgTimerTicks = avgTimerTicks + (currentTimerTicks - prevTimerTicks)
numTimerExpirations = numTimerExpirations + 1
prevTimerTicks = currentTimerTicks
End If
End Sub
Private Sub formsTimer_Tick(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles formsTimer.Tick
If timerRunning = False Then
formsTimer.Enabled = False
formsTimer = Nothing
Else
Dim currentTimerTicks As Long = Environment.TickCount
avgTimerTicks = avgTimerTicks + (currentTimerTicks - prevTimerTicks)
numTimerExpirations = numTimerExpirations + 1
prevTimerTicks = currentTimerTicks
End If
End Sub
End Class