Updating UI from asynchronous method

tfowler

Regular
Joined
Aug 16, 2005
Messages
84
Location
Columbus, OH
I am writing a data acquisition application for our sensor testing and calibration area and have run into a problem. I had the application working great using a single-thread with Do Events in the looping structures, but didn't like the responsiveness of the UI.

So, I did some research and came across the MSDN Magazine Basic Instincts articles on delegates and updating the UI from a secondary thread. It is a great series of articles, which gave me good insight into what I wanted to do. However, implementing it in my application is causing me all kinds of headaches.

I use the following method to update my UI from the data acquisition asynchronous method:

Visual Basic:
    '---------------------------------------------------------------------
    'update the user interface with the status
    '
    'status - the status text to display in the StatusBar
    'state - the state of the data acquisition when this call was made (e.g.
    '   "Running", "Completed", or "Cancelled")
    '---------------------------------------------------------------------

    Public Sub UpdateUI(ByVal status As String, ByVal state As String)
        If Me.InvokeRequired Then
            'need to switch to the primary UI thread to perform update
            Dim handler As New UpdateUIHandler(AddressOf UpdateUI)
            Dim args() As Object = {status, state}
            Me.BeginInvoke(handler, args)
        Else
            'on primary UI thread, go ahead and update
            sbStatus.Text = status
            If state = "Completed" Then
                btnSave.Visible = True
                btnRetry.Visible = True
                Beep()
                btnSave.Focus()
            ElseIf state = "Cancelled" Then
                Close()
            End If
        End If
    End Sub

It works great when I update the sbStatus text using the command
Visual Basic:
frmDataAcq.UpdateUI("Change in status text", "Running")
. I can update the status text repeatedly with no problems. The problem occurs when the asynchronous method finishes and the UpdateUI method is called from the following callback method:

Visual Basic:
    '------------------------------------------------------------------
    'called when data acquisition process being run on secondary thread
    'completes
    '
    'ar - arguments passed from process
    '------------------------------------------------------------------

    Private Sub AcquisitionCallBack(ByVal ar As IAsyncResult)
        Try
            Dim returnValue As Boolean
            returnValue = AcquisitionHandler.EndInvoke(ar)
            If returnValue = True Then
                UpdateUI("Finished", "Completed")
            Else
                UpdateUI("Finished", "Cancelled")
            End If
        Catch ex As Exception
            Dim message As String
            message = "Error: " & ex.Message
            UpdateUI(ex.Message, "Cancelled")
        End Try
    End Sub

The application hangs at the line
Visual Basic:
btnSave.Visible = True
in the UpdateUI method. I have read everything I can find by Googling and cannot figure out what I am doing wrong. :confused:

Thanks for any insight you can provide,

Todd

Edit :confused: :
Seems to be rather random.
 
Last edited:
Temporary Hang

Ok, the problem seems to be a temporary hang, like the UI thread is waiting for something else to happen before anything can run on it. It is really strange (which I guess is expected with multithreading). It seems to be random...sometimes it won't hang, other times it can hang for 5-10 minutes before it finally updates the UI. Again, this only happens when the callback method is called, so the secondary thread should be finished. If I'm just sending updates to the UI, everything runs fine.

I appreciate any help I can get,

Todd
 
Can it be that anything else is updating/using the UI at the same time?
The code in your first post looks correct (although you might want to try Invoke() instead of BeginInvoke() as BeginInvoke allocates some resources that are only cleared if you call EndInvoke() I believe).

Only if another event can also trigger a UI update (and if it takes those 5-10 minutes ;) ) might that explain the 'hang' I think. It could also be a long process triggered by the user that isnt handled by a separate thread.
 
Solved...for now

Wile said:
Can it be that anything else is updating/using the UI at the same time?
The code in your first post looks correct (although you might want to try Invoke() instead of BeginInvoke() as BeginInvoke allocates some resources that are only cleared if you call EndInvoke() I believe).

Only if another event can also trigger a UI update (and if it takes those 5-10 minutes ;) ) might that explain the 'hang' I think. It could also be a long process triggered by the user that isnt handled by a separate thread.

Thanks for the help Wile. I tried the Invoke() instead of BeginInvoke() but there was no difference. I also checked through all my code to make sure I wasn't accessing UI objects from outside the UI thread and did not see any issues.

Finally, one of the other programmers here opened the code on his machine (using VS2003, while I was using VS2002), it it ran fine...multiple times. So, I went ahead and upgraded my machine to VS2003, and it works fine. I was waiting to upgrade until 2005 was released, but oh well.

If it is race condition, it still might not be completely fixed, maybe the timing just changed...but I'm going to continue as if it is fixed and hope nothing changes. At least this is an internal application...so I get to test it on the production computers. :-\

Todd
 
tfowler said:
Finally, one of the other programmers here opened the code on his machine (using VS2003, while I was using VS2002), it it ran fine...multiple times. So, I went ahead and upgraded my machine to VS2003, and it works fine. I was waiting to upgrade until 2005 was released, but oh well.

Do you know if the .net framework was also upgraded? VS2003 probably installs 1.1 if not installed yet, it might be that vs2002 uses 1.0 by default. Maybe MS fixed something in between 1.0 and 1.1 regarding this ;).
 
Wile said:
Do you know if the .net framework was also upgraded? VS2003 probably installs 1.1 if not installed yet, it might be that vs2002 uses 1.0 by default. Maybe MS fixed something in between 1.0 and 1.1 regarding this ;).

Yeah. I had actually already installed 1.1 KB886903 while troubleshooting an ASP.NET issue a couple of months ago. VS2002 would only use 1.0, so hopefully the threading issue was a fix in the latest version of 1.1. Keeping my fingers crossed.

Thanks,

Todd
 
PlausiblyDamp said:
Are you calling into any COM components as part of the non-UI code? IIRC .Net 1.0 had 'issues' with the COM threading model, these were fixed in 1.1

No. Everything I'm using should be .NET managed code. However, I am accessing drivers and SDK for National Instruments GPIB cards in order to perform the data acquisition. I am using their latest .NET SDK, but you never know about NI. We've had problems with their drivers before. :-\

Thanks for the input,

Todd
 
Last week, I finally figured out what I was doing wrong here and figured I post the solution for everyone else.

I was updating a DataSet from the secondary thread that was attached to a DataGrid. It appears that this causes the same problem as updating any other UI element from a secondary thread.

So, I created an event in the secondary thread that passes an array of data back to the UI thread. Then the UI thread updates the DataSet. Its a very sneaky problem that had me frustrated for a long time.

Todd
 
Back
Top