Easy UI threading with Background Worker |
Written by Mike James | ||||||||
Friday, 01 October 2010 | ||||||||
Page 1 of 5 Given the single threaded design of the Silverlight/WPF UI and the abhorrence of DoEvents you have little choice but to conclude that two threads are mandatory in any Silverlight/WPF application. The problem is how best to work with them. The BackgroundWorker class can be used to implement a completely general application architecture - find out how. There is a fundamental problem with the design of Windows Forms, WPF and now Silverlight when it comes to the way the UI is structured. The big problem is that in all three cases the UI is single threaded and the UI components are not threadsafe. When you first start using the UI frameworks this doesn't seem like a huge problem and in the case of Windows Forms it is partly alleviated by the ability to use the DoEvents construct. However DoEvents is not an easy option in the case of WPF or Silverlight, so you have to think of more sophisticated solutions much earlier. In fact you could say that a multi-threaded program is the norm in WPF and Silverlight because, without a multi-threaded architecture, you are just waiting for problems to occur. Consider the reasons why. The UI thread responds to all UI events and is the only thread that can safely create and manipulate UI components. When the UI thread isn't busy running an event handler it is idle and other threads get to run. Now consider an application that does some work in response to the user's input. Suppose the user clicks a button and the UI thread responds - then while the UI thread is processing the button click it can't respond to another UI event. In short the UI is frozen while your application does some work. You might think that this is ok as the user couldn't possibly want to interact with the applications UI until the task they started is complete - but there is nothing quite like a frozen application to convince users that it has crashed. This is not a good way to design things. DoEventsSo how is it possible that we got away with single threaded applications in the days of Visual Basic 6 and even modern Windows forms applications? The answer is simple - DoEvents. The DoEvents command allowed the UI thread to stop processing the current eventhandler and return to the message queue to see if there is anything to do. If there was something to do then the UI thread processed all of the pending event messages until the message queue was empty before returning to your suspended code. Notice that your processing code is blocked until the UI has been serviced. The problem with DoEvents is that you can't really predict where your code will go after you use it. The user can press any buttons or click on any menu option and the UI thread can hence enter any event handler - including the one that is suspended by the DoEvents command. This non-deterministic flow of control means that you really need to make your application threadsafe - and this isn't easy. Clearly you need to manually build some control into the situation - at the very least you have to disable the event that invokes the event handler that has yielded via a DoEvents command. For simple applications it is possible to ensure that DoEvents doesn't make a mess of your flow of control and as long as you don;t use DoEvents in more than one event handler and remember to disable that event before the DoEvent then you have to try quite hard to create a problem. As long as you understand how it all works DoEvents is a perfectly valid way of keeping the UI responsive and it has the huge advantage of being simple. And as we shall see you end up with as many, if not more, problems when you attempt to use multiple threads. Threading is dangerousIf you reject DoEvents then you have no choice but to use multiple threads to keep the UI responsive. There is also the argument that in this day of multicore processors not using multiple threads is just not making the best use of the hardware. There is a general feeling about that threads are inevitable. You could take the position that the UI thread should never do any computation other than service the UI. What this means is that every WPF or Silverlight application has to be multi-threaded. The big problem is that threads are hard. Event the standard documenation has health warning notices around threading:
Not only is there much greater potential for deadlock and race conditions than you are likely to encounter with DoEvents sharing the UI between multiple threads is complicated. In fact it makes DoEvents look like a safe option. Cross threadingIn an attempt to make the UI a safer place Microsoft implemented a mechanism that only allows the thread that created a UI component to access it. That is each UI component records the identity of the thread that created it and checks that only that thread acesses it - it throws an exception if another thread attempts to use it. In practice it is the UI thread that creates UI components and so the rule effectively means that only the UI thread can access the UI. A better way of dealing with this situation would have been to make the UI components thread safe and provide a mechanism for locking. You can attempt to implement this if you want to, as the "UI thread only" mechanism can be turned off - but it is not an easy option without built in locking. If you want to use multiple threads and allow them to also manipulate the UI you have to implement some sort of interthread communication and the simplest and most direct is the Invoke pattern. <ASIN:0470534044> <ASIN:1430224258 >
<ASIN:0672330628 > <ASIN:0470524650 > <ASIN:1430229888 >
|
||||||||
Last Updated ( Friday, 01 October 2010 ) |