Asynchronous Error Handling (DotNet)

Soldato
Joined
5 Mar 2003
Posts
10,771
Location
Nottingham
Hi all,
Just wondering if there is a "standard" to handle the following situation.

I have a GUI which calls SQL queries in another thread. These queries can run for a long time and possibly time out. The problem I have is that when there is an exception the whole app crashes (as the exception isnt being handled in the new thread). Now it's fairly simple to correct this issue, however I want the user (i.e. GUI) to be notified of the error and display it to the users.

Now to my knowledge there are a few options:
  • Events - The event would return the result and also some args to specify the state etc (complete, cancelled, error).
  • Async Delegates - This is my current implementation and just returns the result. To make this handle errors correctly, I would need to wrap the result and any error information into a "bigger" class.
  • Polling - The "worker" thread could actually not terminate and the "main" thread could just poll states on the worker thread to see if it has a result and if not check for state.

My idea situation would be one where the worker thread could put the exception on the main thread before exiting, however I dont think this exists (exceptions are stack-bound objects. Since every thread has its
own stack, an exception thrown in thread A cannot suddenly appear in
thread B).

So whats the best option here? Just to be clear I know how to handle this situation but not the *best* way.

Thanks in advance!
 
Personally I would use an event based methodology. I would have an sql class with an event named complete which is passed a class the wraps up the result, attach to that a function which checks if invokerequired etc etc so that the worker thread hands back processing to the gui thread which can process the results.
 
Well the exact approach I'd take would depend on exactly how you're running the queries. If you're just creating an anonymous method with the query logic in it and then passing that as the starting method to a new thread, then just put a try/catch block in it. In the catch block, have some code which invokes a method on the UI thread using the Form.Invoke method.

As ThieveryCorp said, though, a BackgroundWorker is probably your best bet if you're firing it from the UI thread.
 
Thanks for the input guys, however I have found a better solution (well at least requires me to do less work). Apparently, and I didnt know this before, if you use delegates and call begin invoke (which is how my code works at present, with async call back) - they you call EndInoke, if an exception has been raised by the worker thread, it is put on the stack of the caller thread (in this case the UI).

Obviously, if I was working on something which would be distributed I would rework the code to implement one of your solutions, as its not wise to have a worker thread bomb out for something that can occur at lot (timeouts / permission issues) but the library will only be used by this GUI.

Anywho - thanks again, and hope this post will help others in the future, as I certainly didnt know about the EndInoke / Exception stuff!
 
Look at the properties exposed by your IAsyncResult parameters of the event handler.

There are some useful ones like "AsyncWaitHandle". So your GUI thread can block on this, displaying a progress barberpole, and then when the handle is "signalled" you know the operation has finished and then you can display the exception (if any).

Thanks for the input guys, however I have found a better solution (well at least requires me to do less work). Apparently, and I didnt know this before, if you use delegates and call begin invoke (which is how my code works at present, with async call back) - they you call EndInoke, if an exception has been raised by the worker thread, it is put on the stack of the caller thread (in this case the UI).
Hmm that doesn't seem quite right. Normally the async delegate pattern doesn't process the End*() on the same thread that Begin*() was called on. Or at least most implementations (certainly in the standard framework) don't provide that guarantee. You can check this by doing a Trace.WriteLine(System.Threading.Thread.CurrentThread.Id) (or something like that) on both the line before you call Begin*() and the line after End*()

The End*() method will actually block, possibly for a long time, and that doesn't matter because the implementation of the async delegate is being run on a seperate thread to the invoking thread.

Most implementations of this pattern will also throw their exceptions on the End*() call, not the Begin*(). Although there is some exceptions to this especially with regards to WinForms - which doesn't actually require you to EVER call EndInvoke() to a corresponding BeginInvoke().
 
An example snippet of code. This is how I push the exception handling to the GUI thread. Probably should have been more clear.

Code:
private void PopulateProviderList(IAsyncResult ar)
        {
            if (ar.IsCompleted && InvokeRequired)
                Invoke(new MethodInvoker(delegate { PopulateProviderList(ar); }));
            else if (ar.IsCompleted && !InvokeRequired)
            {
                List<Provider> providerList = null;
                try
                {
                    // Get a handle on the original delegate and get the results.
                    ProviderDelegate providerDelegate = ((AsyncResult)ar).AsyncDelegate as ProviderDelegate;
                    providerList = providerDelegate.EndInvoke(ar);

                    if (providerList != null)
                    {
                        // Insert the "all providers option" and bind the results to the cbo box.
                        providerList.Insert(0, new Provider("All Providers", -1));
                        cboProviders.Enabled = true;
                    }
                    else
                        throw new ApplicationException("Provider list was null.");

                    cboProviders.DataSource = providerList;
                    Status = "Ready.";
                }
                catch (Exception ex)
                {
                    // An exception has been raised from the worker thread.
                    Status = "Could not populate provider list: " + GetOriginalErrorMessage(ex);
                    providerList = new List<Provider>();
                    providerList.Insert(0, new Provider("Error populating list.", -1));
                    cboProviders.DataSource = providerList;
                    UpdateEnabledStatus(false);
                }
            }
        }
I thought async wait actually blocks the current thread (i.e. would make the GUI unresponsive)?
 
Back
Top Bottom