Easy UI threading with Background Worker |
Written by Mike James | ||||||
Friday, 01 October 2010 | ||||||
Page 2 of 5
The Invoke patternThe pattern that tends to be used is to create additional threads which do the work of the application and provide access to the UI - after all worker threads have to present their results at some point - using the Invoke mechanism or to use an interthread communication mechanism to pass results to the UI thread directy. Of the two the invoke pattern is the most often used because it requires the least amount of custom code. The Invoke pattern is described in detail in Deep Threading the gentle use of invoke but to put things simply a thread can call Invoke or BeginInvoke to pass a delegate to the UI thread to execute. That is the inter-thread communication is achieved by one thread asking the UI Thread to run a procedure of its choice complete with parameters. Invoke is a blocking synchronous call and is much safer than BeginInvoke which is non-blocking asychrounous. Often Invoke and Begininvoke are presented as if they are safe options - they aren't. The Invoke mechanism gives you the ability to ask the UI thread to run a delegate and so give it access to the UI components from a non-UI thread - but if that delegate accesses variables created by the worker thread then there is the usual potential for race hazards and deadlocks. If you are using BeginInvoke then you are safe if you only use value types passed on the stack. Any attempt to access non-UI reference types or global types from within the invoked delegate is dangerous and you need to use locking. In the case of Invoke the worker thread is at least suspended while the UI thread gets on with using what ever it wants. In this case the only problem arises if there are multiple worker threads sharing a set of variables. In the case of BeginInvoke you have to be very careful not to fall into the trap of allowing the UI thread and the worker thread . So the Invoke pattern is fairly safe but BeginInvoke is as dangerous as full multi-threading. The BackgroundWorker patternThere is another problem with the Invoke method - its very messy. You have to create delegates, start and stop threads, locate the correct despatcher object and run Invoke or BeginInvoke. Its messy even before you get to worrying about synchronisation and locking. The average developer may not understand threading well enough but they were brought up on event handling. The BackgroundWorker pattern converts a problem in threading into one of event handling - although you can't completly ignore the fact that there are at least two threads in operation. The BackgroundWorker class can be found in using System.ComponentModel; and the key things you need to know about it is that it has three events:
When you use BackgroundWorker you usually define all three event handlers.
If you have been following the disucssion you should be able to guess that the DoWork event handler runs via its own thread but the ProgressChanged and RunWorkerCompleted event handlers run on the UI thread as they have access the UI. Although the ProgressChanged event handler sounds as if its role in life is simply to update a progress bar or some other progress indicator you can see that it can be put to a wider purpose. The DoWork event handler can fire the ProgressChanged event anytime it wants to by calling the ReportProgress method. It can also pass to the ProgressChanged event handler a single integer which is supposed to indicate the percentage completed and an object which can contain any information it cares to package up. Clearly the ProgressChanged event handler can perform a general UI update and not just a progress indicator. So to use the BackgroundWorker pattern you:
If you agree that every WPF/Silverlight application should be multi-threaded then what we now have is a blueprint for ever such application. Let's try it out. <ASIN:0470548657> <ASIN:047050224X> <ASIN:0071668950> <ASIN:0470563486> <ASIN:1430272058>
|
||||||
Last Updated ( Friday, 01 October 2010 ) |