我有一个棘手的问题(至少对我而言)。我正在研究用VB.net编写的Windows服务。我正在使用System.Timers.Timer类来定期调用委托方法以查看是否有任何工作要做。处理的时间并不重要,我试图通过在调用方法时立即禁用Timer并在结束时再次启动它来阻止重新进入worker方法。
但是,Timer类Elapsed事件发生在不同的线程上。在线搜索,大多数人都在使用实现ISynchronize接口的Windows表单来编组回调到原始线程的调用。理想情况下,我不想使用Windows窗体来实现此目的。有没有一种简单的方法可以将呼叫重新定向回原始线程?
或者是否有一个我可以继承的框架类来执行此操作?或者最坏的是ISynchronize的简单实现?
Imports System.Timers
Imports System.IO
Imports System.Data
Public Class Application
Implements IDisposable
Private WithEvents _Timer As Timer
Private Sub SleepTimerCallback(sender As Object, e As ElapsedEventArgs) Handles _Timer.Elapsed
' TODO need to find a way to bring this method back on the main thread.
' Temporarily disable the timer elapsed event so that we don't have event re-entrance.
If Me._Timer.Enabled = True Then Me._Timer.Enabled = False
' Do Work.
' Re-enable the timer elapsed event.
_Timer.Enabled = True
End Sub
End Class
答案 0 :(得分:2)
您的计时器启用/禁用代码有一个微妙的错误。你有:
If Me._Timer.Enabled = True Then Me._Timer.Enabled = False
' Do work
' Re-enable the timer
_Timer.Enabled = True
因此,如果在输入时禁用了计时器,则代码仍会执行。当然,你不应该接到多个电话,但你的条件检查基本没用。
更好的方法是在AutoReset
设置为False
的情况下初始化您的计时器。这使计时器只勾选一次。然后,在事件处理程序结束时,再次调用Start
以重新启动计时器。这样你就不可能获得对处理程序的多个并发调用。
System.Timers.Timer
具有压缩异常的不幸特性:
Timer组件捕获并抑制事件处理程序为Elapsed事件抛出的所有异常。
因此,如果您的事件处理程序抛出异常,您将永远不会知道它。除了不会重新启用计时器。所以你需要写:
Private Sub SleepTimerCallback(sender As Object, e As ElapsedEventArgs) Handles _Timer.Elapsed
Try
' TODO need to find a way to bring this method back on the main thread.
Finally
' Re-enable the timer elapsed event.
_Timer.Start()
End
End Sub
你也可能想在那里处理异常。否则你永远不会知道它们会发生。
答案 1 :(得分:1)
将来自工作线程的调用编组到特定其他线程并非易事。只有某种线程可以支持这一点。 Winforms或WPF应用程序中的主线程符合条件,它们是特殊的,因为它们具有调度程序循环。 producer-consumer problem的通用解决方案。
有一个很好的理由他们需要这样做,用户界面基本上是线程不安全的。您只能从创建它的线程更新UI。因此,始终可以获取在特定线程上运行的代码非常重要。
服务完全缺少此基础结构。它甚至没有“主线”的概念。它不需要它,没有UI。因此,您不需要编组调用,只需让您的Elapsed事件处理程序完成工作。
请注意,您必须处理一些细节才能使Elapsed事件处理程序安全。您应该将计时器的AutoReset属性设置为False,以确保在事务处理程序仍处于忙碌状态时不会再次调用它。这很难达到目的。您已经自己做过,但使用AutoReset更好。并且您应该始终使用Try / Catch来捕获异常。 Timer类有一个讨厌的习惯,即在没有诊断的情况下吞咽异常,你的服务将停止运行,你将不知道为什么。在Catch子句中记录异常,这样您就会知道它停止的原因。