问题
Recently, I've been working with a system.form.timer in the UI thread. I've have noticed that while I can stop a timer on a background thread, I cannot start it back up unless I use BeginInvoke even though I do not receive a cross-threading exception. On a system.timers.timer however it seems that I can stop and start it from the background thread created by the timer. Why is this? Is a system.form.timer allowed to be stopped, but not enabled from a background thread? This seems a bit odd to me.
System.Form.Timer Code
DOESN'T WORK
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
WORKS
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 Code
WORKS
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
回答1:
The Winforms Timer class is somewhat thread-safe, not enough to keep you happy. When you call its Start() method then it creates a hidden window that turns WM_TIMER messages into Tick events.
When you do this on a worker thread then you tend to have a problem, these WM_TIMER messages are only dispatched when the thread runs a message loop. Worker threads don't normally do this, they don't call Application.Run(). So the timer just won't tick.
Calling the Stop() method is otherwise okay, it knows how to find that hidden window back even through the code runs on the wrong thread. The workaround you found with BeginInvoke() works because it now correctly calls Start() on the UI thread and gets the hidden window created with the correct owner thread, one that pumps. System.Timers.Timer doesn't have this problem, it doesn't rely on WM_TIMER to tick but uses a System.Threading.Timer instead. Which is supported by a dedicated worker thread managed by the CLR. Do note that this timer is pretty dangerous. Calling MsgBox() on a worker thread is fundamentally wrong, for one. High odds that the user never sees it since it will be behind a UI window.
That explains it, I can't otherwise offer better advice since I can't see what you are really trying to do. Be careful, you are playing with fire.
来源:https://stackoverflow.com/questions/18545787/timer-can-be-stopped-in-backgroundworker-but-cant-be-started