DarksideCookie

Come to the dark side...we have cookies!

Multithreading my way

Ok, so Silverlight is a very cool technology, and Microsoft has done a whole lot of things to make sure that it performs the way it should. They have done things like forcing you to run long running tasks, such as webservice calls, in an asynchronous fashion. But if you start doing long running tasks on your own, you need to make sure to handle the multithreading yourself. Why? Well, if you don’t, you will perform all of that stuff on the UI thread.

And why is performing heavy things on the UI thread a bad thing? Well…it just is!

Ok, so I will explain a bit more. I assume that most of you already know this and can skip this paragraph, but if you don’t, keep reading. The UI thread is responsible for any interaction with the user. It means responding to mouse clicks and things like that. If you have ever seen a windows application lock up and become unresponsive, it is because the UI thread has been locked and doesn’t handle its messages as it should. This causes the application to become unresponsive. In Silverlight, this gets even funkier. Because, if you lock up your UI thread, you don’t just lock up your Silverlight app, you actually end up locking up the whole browser, causing the whole browser to freeze. Of course this will unfreeze as soon as your operation is done, but you still have to handle this.

So how do you handle long running tasks? Well, there are several ways and some of them depend on what you need to do.  Neither of these are Silverlight specific in any way. They can be used in any .NET situation!

If you only need a “fire and forget” kind of execution, that is you need something to be done, but you don’t care when it gets done and don’t care about the result of the execution, then you should use the ThreadPool. The ThreadPool is sort of a bunch of threads hanging around, waiting for you to need them. So you tell the pool that you have some work that you need done, and as soon as there is a thread available, that thread will do it for you. The syntax is fairly simple

ThreadPool.QueueUserWorkItem((state) => {
// Do what needs to be done
});

As you can see, it is just a call to a static method, and if you want to, you can also pass along some state to it. And yes, I am kind of deceiving you when I said that you would not get a result back when it was done. Obviously you could have the code running running in the background notify you when it was done. Just remember that it would be notifying you on another thread. So you need to handle the potential threading issues on your own.

And one more thing to note, whatever goes on in the execution above is happening on a background thread (yes I have said that several times). And that matters if you try to change some UI stuff. The UI can only be touched by the UI thread. So you need to marshal it to that thread using the current Dispatcher.

Ok, so what is wrong with the ThreadPool way of doing multithreading? Well, not a whole lot, except for the fact that you do not know when it runs, and you do not by default get notified when it is done, and a bunch of other things like the fact that you cannot abort it and so on. As soon as it goes on the queue, you loose control.

Unfortunately, getting that extra control means a bit more work. It means creating a new Thread manually. When you do this, you have a reference to the thread and can see if it is still alive, abort it, access its thread state, join it and so on.

The code for creating a new thread is simple. Just create a new instance of Thread, and hand it a delegate that either takes no parameters or an object as the only parameter. Then call the Start() method.

var t = new Thread((parameter) => {
// Do your stuff
});
t.Start();

And as much control you get with this, it still has some of the annoying features that the ThreadPool has. Most of these have to do with the fact that it runs on a background thread, so in general it can be solved by working with the Dispatcher.

Normally though, I tend to use the newest addition to the threading world, the BackgroundWorker class. The BackgroundWorker class is built for just these kind of scenarios. It runs your code on a background thread, just like the previous examples, but it has a major difference. It was built for things like this. It will run your long running code on a background thread, but notify you when it is done on the thread that started the operation, which is often the UI thread. It supports cancelling the execution (sort of), as well as progress reporting and a few other cool things. And all of this in a thread safe manner.

It works using events. So you create a new BackgroundWorker class, hook up a handler to its DoWork event, as well as one to the RunWorkerCompleted event, and then call the RunWorkerAsync() method. And if you want progress reporting, then you hook up a handler to the ProgressChanged event. You can read more about the BackgroundWorker here: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx and here: http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx.

In general, it looks like this

var worker = new BackgroundWorker();
worker.DoWork += (s, e) => {
// Do work
};
worker.RunWorkerCompleted += (s, e) => {
// Completed
};
worker.RunWorkerAsync();

The nice feature of this class, is the fact that it runs the RunWorkerAsync event on the initiating thread. This means that in a lot of cases we get around some Dispatcher calls. This might still be needed in some scenarios, but it helps out in a lot of cases.

Having used this class a lot, I find the hooking up event handlers and stuff is a bit annoying. And on top of that, I rarely use the abort functionality or the progress reporting. Why? Well, often my long running tasks are either not THAT long running, or have some problems supporting abortion and progress reporting.

So to make this more efficient, I have encapsulated all the functionality in a helper class I call BackgroundExecution. It enables me to get the BackgroundWorker functionality with a single method call that looks like this

BackgroundExecution.Execute(() => {
// Do work
}, (result) => {
// Callback when done
if (result.Exception != null)
{
// Handle exception caused by execution
return;
}
});

As you can see, it becomes a lot cleaner, and even handles errors. The parameter passed to the second delegate is of a type called BackgroundExecutionResult. It has a single property called Exception, and if there is an exception while running the workload, it will be passed to the second delegate using this property.

So all of the sudden the multithreading has been contained to a single method call. But how is the BackgroundExecution class implemented? Well, it isn’t that complicated. First there is the BackgroundExecutionResult class

public class BackgroundExecutionResult
{
internal BackgroundExecutionResult()
{
}

internal BackgroundExecutionResult(Exception ex)
{
Exception = ex;
}

public Exception Exception
{
get;
private set;
}
}

As you can see, it is very simple. And the static Execute() method isn’t that much more complicated.


public static void Execute(Action workload, Action<BackgroundExecutionResult> callback)
{
BackgroundWorker worker = new BackgroundWorker();

RunWorkerCompletedEventHandler completedHandler = null;
completedHandler = (s, e) =>
{
worker.RunWorkerCompleted -= completedHandler;
if (e.Error != null)
{
callback(new BackgroundExecutionResult(e.Error));
return;
}
callback(new BackgroundExecutionResult());
};
worker.RunWorkerCompleted += completedHandler;

DoWorkEventHandler workHandler = null;
workHandler = (s, e) =>
{
worker.DoWork += workHandler;
workload();
};
worker.DoWork += workHandler;

worker.RunWorkerAsync();
}

It does little more than hook up the handlers to a new BackgroundWorker instance. And then it makes sure to call the callback method when complete, passing along any exception caused by the workload.

But what if the workload needs to return a result. Well… It can. I have an generic overload to the Execute() method. It makes it possible to pass a typed result to the completed handle. The call looks like this

BackgroundExecution.Execute<bool>(() =>
{
// Do Work
return true;
}, (result) => {
if (result.Exception != null)
{
// Handle
return;
}
bool b = result.Result;
});

As you can see, it is just as simple. The workload just has to return a value of the type defined by the generic type. And in the second callback, I can read the result in a nicely typed way.

Implementation wise, this is really not that complicated either. First, I have sub classed the BackgroundExecutionResult and added a generic property

public class BackgroundExecutionResult<T> : BackgroundExecutionResult
{
internal BackgroundExecutionResult(T result)
{
Result = result;
}

internal BackgroundExecutionResult(Exception ex)
: base(ex)
{
}

public T Result
{
get;
private set;
}
}

And then I have overloaded the Execute() method with a generic version. This means making the first parameter a Func<T> instead of an Action, and the second parameter stays an Action, but changes to a generic Action that takes an BackgroundExecutionResult<T>

public static void Execute<T>(Func<T> workload, Action<BackgroundExecutionResult<T>> callback)
{
BackgroundWorker worker = new BackgroundWorker();

RunWorkerCompletedEventHandler completedHandler = null;
completedHandler = (s, e) =>
{
worker.RunWorkerCompleted -= completedHandler;
if (e.Error != null)
{
callback(new BackgroundExecutionResult<T>(e.Error));
return;
}
callback(new BackgroundExecutionResult<T>((T)e.Result));
};
worker.RunWorkerCompleted += completedHandler;

DoWorkEventHandler workHandler = null;
workHandler = (s, e) =>
{
worker.DoWork += workHandler;
e.Result = workload();
};
worker.DoWork += workHandler;

worker.RunWorkerAsync();
}

That’s all there is to it. It makes running calls asynchronously so easy that there is no reason not to!

But…all the other asynchronous support stuff enables you to pass a state object to the workload code, don’t we need this in the BackgroundExecution case as well? Well, you can add this without any problems at all, but I don’t think you will need it. As we can make the calls using lambda expressions, we can access variables around the call, making the state object less usable. Just be a bit careful when you access variables from inside the workload lambda as it runs on a separate thread…

I would love to hear any feedback you have about this way of doing multithreading. So far I haven’t had any issues when using it, but it has made my code a lot easier to read and work with. It also makes me a lot more likely to use multithreading even in simple situations.

Cheers!

Posted: Feb 21 2011, 01:05 by ZeroKoll | Comments (4) |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Manage post: :)

Comments (4) -

Pablonete Spain said:

I like your BackgroundExecution class, but I would have implemented it with ThreadPool + Dispatcher.BeginInvoke (offering the same public interface). Do you know pros or cons about this? I think BackgroundWorker, as a component, is heavier.

# March 03 2011, 01:14

ZeroKoll New Zealand said:

Hi Pablonete!
I agree that it is a bit more heavy weight as it adds a bunch of helpful things such as automatic marshaling of the events to the calling thread and so on. Things that you need, so you might as well use it when it is already there.
I personally don't believe that you should re-invent the wheel if you don't have a very good reason to. In this case, you would have to handle thread marshaling and other things manually if you choose the thread pool.
The performance gain that you get is most likely going to be tiny. So unless you start hundreds of background executions at once, I don't see that you will see any performance gain.
That's what I think at least...

# March 03 2011, 09:26

Pablonete Spain said:

OK, I agree with you. And afterall you can improve later the implementation while keeping public interface unmodified.
My intention was only to know if BackgroundWorker makes something that can't -or hardly- be done with ThreadPool + Dispatcher.BeginInvoke.
BTW I buy your idea and will try to test with two approaches.

Cheers.

# March 03 2011, 09:38

ZeroKoll New Zealand said:

Hi again!
I don't think the BackgroundWorker does anything that you can't do on your own. It would surprise me a WHOLE lot. And I don't even think it would be that hard to build your own implementation.
Cheers!

# March 03 2011, 10:42

Pingbacks and trackbacks (1)+

Comments are closed