【发布时间】:2015-09-17 18:54:24
【问题描述】:
我有一个创建设备监控线程的 VB.NET 应用程序。 MonitorThread 是一个“无限”循环,它通过阻塞函数DeviceRead() 等待设备数据,然后用数据更新表单控件。当设备停止时,DeviceRead() 返回零,这会导致 MonitorThread 终止。这一切都很完美。
问题是这样的:在FormClosing()中,主线程暂停设备,然后调用Join()等待MonitorThread终止,但Join()永远不会返回,导致应用程序挂起。永远不会到达 MonitorThread 末尾的断点,这表明 MonitorThread 不知何故被饿死了。但是,如果我在Join() 之前插入DoEvents(),那么一切都会按预期工作。为什么需要DoEvents() 来防止挂起,有没有更好的方法来做到这一点?
我的代码的简化版:
Private devdata As DEVDATASTRUCT = New DEVDATASTRUCT
Private MonitorThread As Threading.Thread = New Threading.Thread(AddressOf MonitorThreadFunction)
Private Sub FormLoad(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
DeviceOpen() ' Open the device and start it running.
MonitorThread.Start() ' Start MonitorThread running.
End Sub
Private Sub FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs) Handles MyBase.Closing
DeviceHalt() ' Halt device. Subsequent DeviceRead() calls will return zero.
Application.DoEvents() ' WHY IS THIS NECESSARY? IF OMITTED, THE NEXT STATEMENT HANGS.
MonitorThread.Join() ' Wait for MonitorThread to terminate.
DeviceClose() ' MonitorThread completed, so device can be safely closed.
End Sub
Private Sub MonitorThreadFunction()
While (DeviceRead(devdata)) ' Wait for device data or halted (0). Exit loop if halted.
Me.Invoke(New MethodInvoker(AddressOf UpdateGUI)) ' Launch GUI update function and wait for it to complete.
End While
End Sub
Private Sub UpdateGUI()
' copy devdata to form controls
End Sub
【问题讨论】:
-
第一个问题做得很好。
-
在 doevents 之前,您是否检查过哪个线路监视器线程实际位于?
-
使用 Invoke() 很危险,很容易出现死锁。当你调用 Join() 时当然会这样做。 Invoke() 无法完成,因为您的 UI 线程卡在 Join 调用中。由于线程卡在 Invoke 调用中,Join 无法完成。死锁城。 DoEvents() 隐藏了问题,它没有解决它,因为时间很关键。仅在必要时使用 Invoke()。你没有,你没有使用它的返回值。所以你可以使用 BeginInvoke() 代替。
-
@Thraka,我不知道如何确定 MonitorThread 位于哪一行,但我认为它在 doevents 之前在 DeviceRead() 中等待,因为没有可用的设备数据(设备已停止)。也就是说,DeviceRead() 应该在 DeviceHalt() 执行后不久返回零,因为 DeviceHalt() 取消了等待。
-
这不可能,UI 线程一次不能执行多个方法。调用队列将调用序列化。而UpdateGUI与死锁无关。只需这样做the right way。
标签: .net vb.net multithreading winforms formclosing