Exception-Handling: BeginInvoke vs ThreadPool.QueueUserWorkItem

Sometimes small changes make have a large impact. An example is the exception-handling difference of Delegate.BeginInvoke and ThreadPool.QueueUserWorkItem.

I’m working on a WPF-client application, which does quite a lot of work in the background. Most of the background work is handled by a special task coordination class. For a long time this class executed tasks with ThreadPool.QueueUserWorkItem. And it worked well:

Action workItem = ()=> { };// work item from the application;
ThreadPool.QueueUserWorkItem(ignored => workItem());
// more stuff for synchronization

As you can imagine, a desktop application crashes sometimes. So there’s a central error-handler, which is invoked when the application crashes:

Everything worked as expected. When a background task crashed, it trigger the error-handler as expected (it triggered also the VisualStudio-debugger).

One rainy day, I improved the task coordination class. Among other changes it exchanged the the ThreadPool.QueueUserWorkItem with a Delegate.BeginInvoke.

Action workItem = () => { };// work item from the application;
var syncHandle = workItem.BeginInvoke(null, null);
// more stuff for synchronization

My tests and the application worked fine with the changes for quite a while. So I continued to improve the application. After a while, I started to notice some strange behaviors. Sometimes some work just wasn’t executed. Then it notices a lot of first chance exceptions in the debugger-output. I got suspicious and started to investigate. Soon I found out that my change from ThreadPool.QueueUserWorkItem to Delegate.BeginInvoke was the issue. Some exceptions in back-ground tasks weren’t processed.

 

clean exception handling ;)

clean exception handling 😉

 

What’s the difference?

As you know, Delegate.BeginInvoke isn’t just running the code in the background. It brings synchronization-mechanisms and the Delegate.EndInvoke-operation with it. And here also starts my exception issue. When a exception happens while executing the Delegate.BeginInvoke, the exception is caught. As soon as you get the result with Delegate.EndInvoke, the caught exception is thrown. I wasn’t aware of that. And unfortunately I didn’t end all tasks with Delegate.EndInvoke!

Action workItem = () =>
                        {
                            throw new ExpectedExeption();
                        };
var syncHandle = workItem.BeginInvoke(null, null);
// later
try
{
    workItem.EndInvoke(syncHandle);
}catch (ExpectedExeption e)
{
    // on the end invoke the exception is thrown! 
    // If you don't get the result, the exception is swallowed
}

My solution was to switch back to the ThreadPool.QueueUserWorkItem. This operation doesn’t do any fancy synchronization for you. It just executes the delegate on the ThreadPool. If something goes wrong, the exception just blows into your face. The passed delegate is responsible to handle exceptions.

For RSS feed readers: I’ve just noticed that the gist-code-examples don’t show up in the feed-view =(. Has someone an idea how to fix that?  Meanwhile you need to read the post on the website.

Tagged on: ,