VB.NET 2010,.NET 4
大家好,
我有一个System.Timers.Timer对象,可以对其已用事件执行一些操作:
Private Sub MasterTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MasterTimer.Elapsed
MasterTimer.Enabled = False
'...work...
MasterTimer.Enabled = True
End Sub
我的问题是,它所做的工作有时会被卡住。部分工作是串行通信,因此它可能会等待某些事情的响应。我已经修改了我的串行通信代码,希望能够解决问题。但是,这个计时器基本上是生产控制应用程序的心跳,如果由于任何原因停止它是非常糟糕的。我认为放入故障安全超时可能会很好,因此,如果“工作”花费的时间太长,计时器可能会重新启用自身并再次尝试。我在考虑这样的事情:
将工作移至子程序并创建委托:
Private Delegate Sub WorkDelegate()
Private Sub Work()
'...work...
End Sub
通过调用委托来调用工作,然后在IAsyncResult上使用WaitOne(timeout)来指定超时:
Private Sub MasterTimer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles MasterTimer.Elapsed
MasterTimer.Enabled = False
Dim workDel as New WorkDelegate(AddressOf Work)
Dim result as IAsyncResult = workDel.BeginInvoke
result.AsyncWaitHandle.WaitOne(CInt(MasterTimer.Interval))
MasterTimer.Enabled = True
End Sub
但是,我的问题是:如果Work()真的被卡在某处,会导致问题吗?因为它会重新进入已经运行的子程序?如果在超时后没有完成,有没有办法中止Work()?换句话说,如果在WaitOne之后result.IsCompleted为假,则停止执行Work()?
我并不是很了解这些东西,所以任何评论都会非常感激。也许有一种完全不同的方法来解决这个我不知道的问题?
提前多多感谢!
我想补充一点:
虽然我打算根据Hans的建议做一些重写,但我已经安排了一天的测试,试图找出这个错误的来源。到目前为止,它只在运行已编译的应用程序时发生。我今天(以及一些昨天)尝试在调试模式下运行时重现冻结,这样我可以添加一些断点并找出正在发生的事情。到目前为止,该程序尚未在调试模式下冻结。我只是想知道调试环境是否有任何不同可以解释这一点。可能只是因为我是“幸运的”,但我的运行时间大约是平均时间长度的三倍,之后程序在运行可执行文件时冻结了......再次,我很无知,但是有什么东西关于可以解释这个的调试环境的独特之处?如果冻结,我会再次更新。
答案 0 :(得分:2)
这在代码的第一行出错了。 System.Timers.Timer类非常糟糕,绝对不能保证调用Stop()会阻止另一个调用。计时器使用ThreadPool.QueueUserWorkItem来调用Elapsed事件处理程序。如果线程池忙,这可能最终排队多次调用,等待从TP调度程序开始运行。停止计时器不会阻止那些等待的线程运行。如果不使用锁定,那些线程就会严重踩到彼此并弄乱你的通信状态。
安全的是System.Threading.Timer,其周期为零,因此您只能获得一次回调。使用Change()方法为计时器充电。
调用委托的BeginInvoke()方法,然后阻止它完成没有意义。只需调用Invoke()。免于烧毁另一个线程。
是的,如果“工作”方法永远不会返回,那么您就遇到了问题。一个无法解决的问题。
如果您避免使用轮询来查看串行端口上是否有任何可用内容,那么很多这种痛苦都会消失。让它告诉你,有一些值得继续的东西。只要接收缓冲区中至少有一个字节,它就会在线程池线程上引发DataReceived事件。使用WriteTimeout属性也是避免在通信协议或设备出现问题时卡住的绝佳方法。专用一个线程并使阻塞读取调用也很有效。