1

最近,我一直system.form.timer在与UI thread. 我注意到虽然我可以在后台线程上停止计时器,但我无法启动它,除非我使用BeginInvoke即使我没有收到cross-threading exception. system.timers.timer但是,似乎我可以从计时器创建的后台线程停止和启动它。为什么是这样?是否system.form.timer允许停止,但不能从后台线程启用?这对我来说似乎有点奇怪。

System.Form.Timer 代码

不工作

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    Dim BW As BackgroundWorker = New BackgroundWorker
    AddHandler BW.DoWork, AddressOf CheckTimer
    BW.RunWorkerAsync()
End Sub

Private Sub CheckTimer()
    Timer1.Stop()
    Timer1.Start()
    MsgBox("Stopped and Started Timer")
End Sub

作品

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    Dim BW As BackgroundWorker = New BackgroundWorker
    AddHandler BW.DoWork, AddressOf CheckTimer
    BW.RunWorkerAsync()
End Sub

Private Sub CheckTimer()
    Timer1.Stop()
    Me.BeginInvoke(New TimerStart(AddressOf TimerStartFunction))
    MsgBox("Stopped and Started Timer")
End Sub

Private Delegate Sub TimerStart()

Private Sub TimerStartFunction()
    Timer1.Start()
End Sub

System.Timers.Timer 代码

作品

Dim aTimer As System.Timers.Timer

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    aTimer = New System.Timers.Timer(5000)
    AddHandler aTimer.Elapsed, AddressOf OnTimedEvent
    aTimer.Enabled = True
End Sub

Sub OnTimedEvent()
    aTimer.Stop()
    aTimer.Start()
    MsgBox("Stopped and Started Timer")
End Sub
4

1 回答 1

5

Winforms Timer 类在某种程度上是线程安全的,不足以让您满意。当您调用它的 Start() 方法时,它会创建一个隐藏窗口,将 WM_TIMER 消息转换为 Tick 事件。

当您在工作线程上执行此操作时,您往往会遇到问题,这些 WM_TIMER 消息仅在线程运行消息循环时才调度。工作线程通常不会这样做,它们不会调用 Application.Run()。所以计时器不会滴答作响。

调用 Stop() 方法是没有问题的,它知道如何找到隐藏的窗口,即使代码运行在错误的线程上也是如此。您使用 BeginInvoke() 找到的解决方法有效,因为它现在可以在 UI 线程上正确调用 Start() 并获取使用正确的所有者线程创建的隐藏窗口,即泵。System.Timers.Timer 没有这个问题,它不依赖 WM_TIMER 来打勾,而是使用 System.Threading.Timer 代替。由 CLR 管理的专用工作线程支持。请注意,这个计时器非常危险。一方面,在工作线程上调用 MsgBox() 是根本错误的。用户永远看不到它的可能性很高,因为它将位于 UI 窗口后面。

这解释了它,否则我无法提供更好的建议,因为我看不到你真正想要做什么。小心,你在玩火。

于 2013-08-31T11:49:00.737 回答