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:
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.
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.
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!
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.
- Bachelor Thesis Finished \o/
- Doctor Who Series 5 *Spoilers*