user6392391
user6392391

Reputation:

Properly closing a form running a loop in a new thread

I have method in my form that is running a do while loop in a new thread.

That for loop exits once a sentinel value is set to true (set by another method that handles Me.FormClosing)

The issue is that when the form closes, I get occasionally get two exceptions.

ObjectDisposedException and ComponentModel.Win32Exception.

How do I properly exit a form without "eating" these exceptions and ignoring them.

Code:

Dim _exit As Boolean

Public Sub test()
    Dim task As Thread = New Thread(
            Sub()
                Do
                    checkInvoke(Sub() a.append("a"))
                Loop While _exit = False
            End Sub)
End Sub

Private Sub checkInvoke(ByVal _call As Action)
    If Me.InvokeRequired Then
        Me.Invoke(Sub() checkInvoke(_call))
    Else
        _call.Invoke()
    End If
End Sub

Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    _exit = True
End Sub

Upvotes: 2

Views: 987

Answers (2)

Martin Verjans
Martin Verjans

Reputation: 4796

Where does the error come from ?

This can be a bit confusing but it actually is pretty logical...

  1. The user (or something else) closes the form.
  2. FormClosing is then called which sets _exit to True
  3. Then the Form closes itself, destroying its handle.
  4. Now, it depends where it sometimes throws an exception :
    • Either the Thread just finished the Invoke or the Loop, check the _exit value then ends the loop, everything goes fine.
    • Either it just began the Invoke, then it calls a method invoking the UI thread to modify something on the form that has just been disposed, no more Handle to this form, leading to ObjectDisposedException

How to prevent this ?

One thing you can do is, in your FormClosing event, wait for the Thread to end, then letting the system close the form :

Private _isFinished As Boolean = False
Private _exit As Boolean = False

Public Sub test()
    Dim task As Thread = New Thread(
            Sub()
                Do
                    checkInvoke(Sub() a.append("a"))
                Loop While _exit = False
                'We inform the UI thread we are done
                _isFinished = True
            End Sub)
End Sub


Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
    _exit = True
    While Not _isFinished
        Application.DoEvent() 'We can't block the UI thread as it will be invoked by our second thread...
    End While
End Sub

Upvotes: 2

meJustAndrew
meJustAndrew

Reputation: 6623

I am not familiar to VB, but I have done a similar thing in c++. The main problem is that the for loop does not finish yet when the form is closing. You can just hide the form, wait for the thread to finish and then close the form. You can use flags tomark the stopping of the paralel thread.

Upvotes: 0

Related Questions