I have a VB.NET app that creates a device monitoring thread. MonitorThread is an "endless" loop that waits for device data via blocking function DeviceRead()
and then updates form controls with the data. When the device is halted, DeviceRead()
returns zero, which causes MonitorThread to terminate. This all works perfectly.
The problem is this: In FormClosing()
, the main thread halts the device and then calls Join()
to wait for MonitorThread to terminate, but Join()
never returns, which causes the app to hang. A breakpoint at the end of MonitorThread is never reached, indicating that MonitorThread is somehow being starved. However, if I insert DoEvents()
before Join()
then everything works as expected. Why should DoEvents()
be necessary to prevent a hang, and is there a better way to do this?
Simplified version of my code:
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