|
|||||||||||||||||||||||
|
|||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThere are many processing operations that will take more than 1/10th of a second to complete, even on today's processors! To avoid tying up the user interface, a common approach is to spawn a new thread and do the work there, whilst sending updates back to a suitable indicator on the UI thread. This is one such indicator, implemented in C# using the .NET framework. The first point of interest is actually nothing to do with the progress indicator at all, but is a very useful class that lives in the private void SpawnSomeWork()
{
ThreadPool.QueueUserWorkItem( new WaitCallback( DoSomeWork ) );
}
private void DoSomeWork( object status )
{
...
}
There are a couple of important caveats about the threadpool. The most important thing to note is that you do not own the thread. You may not even be the only piece of work executing on that thread. So - you should not abort or interrupt the thread, or otherwise mess about with its priority. This means that you will always be executing as a background thread, and your code will only get executed when a slot in the threadpool comes free. If those things are important to you, you should manage your own thread instead. You'll notice that the So, having found a way of getting our work done on another thread, we now need to get the progress updates displayed in some sort of UI. To achieve this, I've created an interface called Let's take a quick look at that interface. /// This defines an interface which can be implemented by UI elements
/// which indicate the progress of a long operation.
/// (See ProgressWindow for a typical implementation)
public interface IProgressCallback
{
/// Call this method from the worker thread to initialize
/// the progress callback.
void Begin( int minimum, int maximum );
/// Call this method from the worker thread to initialize
/// the progress callback, without setting the range
void Begin();
/// Call this method from the worker thread to reset the range in the
There are operations that allow the worker to indicate the start [ Another option would have been to create an event interface that the worker was required to implement. (Concrete progress indicators could then consume these events and update their UI appropriately). The advantage of the event approach is that it would allow you to multicast your progress to several subscribers, without further code. I prefer to use the callback approach, and then potentially multicast back out using events, once it has all been marshalled back onto the UI thread (see below for more information on this...) We can now write a worker method that uses this interface to update the user on its progress. private void SpawnSomeWork()
{
IProgressCallback callback; // = ???
System.Threading.ThreadPool.QueueUserWorkItem(
As you can see above, we have added a Note that if we hadn't passed an object that implements Notice also how we are dealing with terminating the worker. We have reworked our algorithm so that it deals with bite-sized packets of work, and after each packet, we check to see if the Having dealt with the client end, we can implement a practical progress indicator dialog based on this interface. In this case, it is called Essentially, Each of the methods in the public interface is going to be called on from the worker thread. However, there are only a smattering of methods on a window that you can call from the non-owning UI thread ( We do this by using the Each operation called from the worker thread (e.g. Here's an example for the /// A delegate for methods which take a range
public delegate void RangeInvoker( int minimum, int maximum );
/// Call this method from the worker thread to initialize
/// the progress meter.
public void Begin( int minimum, int maximum )
{
initEvent.WaitOne();
Invoke( new RangeInvoker( DoBegin ), new object[] { minimum, maximum } );
}
private void DoBegin( int minimum, int maximum )
{
DoBegin();
DoSetRange( minimum, maximum );
}
private void DoBegin()
{
cancelButton.Enabled = true;
ControlBox = true;
}
private void DoSetRange( int minimum, int maximum )
{
progressBar.Minimum = minimum;
progressBar.Maximum = maximum;
progressBar.Value = minimum;
titleRoot = Text;
}
There's a couple of things to note about this. Firstly, notice how the parameters are passed to We also block the worker thread by waiting for an event that is signalled by the All the remaining methods follow a similar pattern, and the end result is a cross-thread progress indication dialog. Typically, clients will invoke it either modally (using I hope you find it useful. History2 Oct 2001 - updated source files to fix some issues. 24 Aug 2003 - updated source files and article to address ThreadAbort() naughtiness, and to include a suggestion that it should be possible to reset the range 'in flight'.
|
||||||||||||||||||||||