Click here to Skip to main content
Email Password   helpLost your password?

Introduction

In any reasonably large application, there are a lot of times when you need to perform a quick task asynchronously. One use off-hand is an application that saves a log or status report to file on start-up. The operation is slow in relative terms since it involves file I/O, and we don't have to worry much about other code paths depending on it. Making it asynchronous will speed up our start time with no risk of side effects.

The .NET Framework makes asynchronous processing quite easy, we'll show a few common ways to accomplish it.  However there are 'gotchas', and keeping track of everything can lead to subtle mistakes and messier code, so I'll introduce a helper class to make our lives easier. 

Background

I'm a sucker for clean and beautifully groomed code. In my experience, maintaining a large code base with few resources is in fact the only way to keep sanity and keep from sliding into a pit of spaghetti. I've been on a push to find areas of necessarily messy or repeated code and find ways to make them more elegant and maintainable - this is the first in a series of small classes and helpers that I kick myself for not doing sooner.

The Obvious and Ubiquitous Solutions

Let's say we have a method as such that we need to run asynchronously:

// The method we need to invoke //
private void SaveReport() {
	statusReport.Save(); //This call takes a bit of time
	Console.WriteLine("Status report saved!");
} 

The first thing many people think of is to create a thread as shown below. Besides the chattiness here, this is not the most efficient way to get this done, unless you have a pretty long running task. For every action, a new thread with context and stack space will be created, just to be used for a short period.

// Starting an async task with a thread //
ThreadStart ts = new ThreadStart(SaveReport);
Thread t = new Thread(ts);
t.IsBackground = true; //important if we don't want this to prevent the app from exiting!
t.Name = "MyThread"; //so we can track it during debugging
t.Start();

It is more effective to utilize the .NET ThreadPool, and let it reuse a few pre allocated threads on your behalf. One way to use the ThreadPool in a round-about way is to make a delegate and begin invocation on it directly.

// Invoking a delegate into the threadpool //
MethodInvoker dlg = new MethodInvoker(SaveReport);
dlg.BeginInvoke(null, null); 

There is a problem with both of those examples as well. The BeginInvoke method takes two parameters, an AsyncCallback for notifying when the operation completes, and an object that is passed through to that callback for the callers purposes, typically the original delegate. Now creating a callback just adds more trouble to this whole exercise, particularly if you do not need to know exactly when the operation completes. However, although not documented well, I've seen issues if the callback is not used to call EndInvoke.

The last way I'll show is to go straight after the ThreadPool.

// Queuing directly into the threadpool //
WaitCallback wc = new WaitCallback(SaveReport);
ThreadPool.QueueUserWorkItem(wc);

This is much more concise, but without the control over the process that we have in the above examples. A larger problem that I haven't discussed is that any of these examples can fail spectacularly, or sometimes silently - any exceptions from your invoked methods will vanish into the ether. Once error handling (and logging, right?) is applied to both the action being performed, and the action of invoking it, and possibly to ending the invoke, we have a lot of code to just make a one-liner action happen safely asynchronous.

If this does fail, you may want to then attempt the action with a dedicated thread to make sure it works if the ThreadPool is not cooperating, that's more code to bandy about.

A more subtle limitation I started to run into is the matter of reentrance. In the above example, we are periodically saving a status report. What happens if it gets delayed? It's quite possible that the next time you invoke it the prior attempt is still in process, all kinds of mayhem could ensue. Now we would need to add even more code for blocking and thread safety for tasks that might get invoked more than once.

The Solution - Using the Code

What if we had some kind of magical helper class that had the power of any of the examples above, but was also incredibly concise? It would put all of this power in one place, so troubleshooting and changes and error handling and tweaks could be concentrated in one place. Come to think of it, such a class might even heal sick puppies! Here's how we would use it.

// Using our Async helper //
Async.Do(SaveReport);

What? How could it be that easy? Is this possible? Yes, all this can be yours, and more. .NET automatically takes any void method with no parameters and invisibly makes a delegate for it, which is how the above works by passing a method name, we can also pass delegates or anonymous methods.

// Using our Async helper with an anonymous delegate//
Async.Do(delegate {
    Console.WriteLine("I'm in an anonymous delegate!");
    statusReport.Save();
});

That saved us from even having to define the first method - just add some code and go.

More Advanced Usage

Now we can get into more functionality, that would take some time to implement for each task normally. With some overloading and fancy footwork, we can also do any of the following..

/* this takes care of any of our locking and blocking and thread-safety woes. 
The ReentranceMode enumeration also has options for 'Stack' and 'Allow' */

Async.Do(SaveReport, ReentranceMode.Bypass); 

We can also track this task and know when it completes, or use the AsyncResult to abort it. The Do method returns a custom class that implements the IAsyncResult interface, with much more functionality than the typical .NET Framework usages.

/* The bool parameter 'useReturnValue' instructs the helper to track 
the return value of your delegate/method if you ask for it later from 
the IAsyncResult that is returned : */

IAsyncResult result = Async.Do(SaveReport, true);
//do other stuff in meantime…
// …and if the action is not completed, wait for it to finish
if (!result.IsCompleted) result.AsyncWaitHandle.WaitOne();
Console.WriteLine("My result was: " + result.ReturnValue);

Instead of defining a method, save time for small tasks by using an anonymous method.

/* Methods that take parameters can be wrapped in an anonymous method, 
the parameter will be psuedo-curryed for us by the framework. 
If the return value is important, 
we can also use a return inside the anonymous method! */

Async.Do(delegate { return GetAnObject("A string parameter"); }); 

And finally, the most advanced overload of the 'Do' method..

/* our most advanced method signature has all of the options, as seen below */
Async.Do(
   SaveReport, //our delegate or method
   false, //track return value?
   this, // a state object to be tracked with the IAsyncResult
   true, /* Use ThreadPool? true will attempt to use the threadpool, 
            then fall back to a thread.
            False always uses a thread, for use with long-running tasks.*/
   ReenteranceMode.Stack); //our enum controlling how we handle multiple 
                           //calls to the same action. 

One last example. What if you need to run multiple tasks and know when they are all complete? A good example of this I found was for closing a collection of network listeners, each one could block for a while so we should close them in parallel but wait for them all to complete before exiting the application.

List wait = new List();

foreach (Listener l in listeners) {
   IAsyncResult result = Async.Do(l.CloseNetwork);
   wait.Add(result.AsyncWaitHandle);
}
//...finish cleaning up a few more things in the meantime....

//and then wait for all of the waithandles to complete.
WaitHandle.WaitAll(wait.ToArray());

Points of Interest

First you might wonder how this class works. I didn't want to get too long in this article, but my blog has a follow-up with all the details of how it was implemented, check it out.

I think ReentranceMode needs a little more attention, as it is a very important feature of this class. The ReentranceMode enum has three options; Allow, Bypass, and Stack.

Allow: In this case, there are no locking checks. If you run a task, and then immediately run it again (possibly from a totally different area of your app) Allow will let them both go at the same time. Find if you aren't accessing any shared resources, say for logging or the like.

Stack: With stackmode, the class will detect if you already have a task executing in the same method (including anon. methods/delegates). It will block before the task begins, and continue once the prior copy completes. Good for shared resources like writing to a file, but be careful — if you get too many stacked up it could balloon your memory usage.

Bypass: The most common that I use, bypass will detect prior instances like stack does, but if it finds one already executing it dumps out immediately without running. This is good for things like internal checks or other tasks that need to run occasionally, but not necessarily back-to-back.

In Part II we will add on to this Async class giving it the functionality to invoke tasks on to the GUI thread as well, making safe multithreading easy in Windows Forms apps.

This solution provides all the power of any other methodology with none of the bloat and maintainability issues. This class has greatly simplified code flow and literally cut thousands of lines of code from a large application I work with, so I hope it can be of use to you as well!

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralVery kool
Donsw
8:19 22 Jan '09  
Great work, I will use it the next tiem I need to. keep up the work.
GeneralGive you five
delong
5:37 18 Dec '08  
Wink
QuestionComparison to BackgroundWorker
spaceboy
7:13 14 Nov '08  
I like this a lot.

Aside from being more concise and the control over reentrancey, how does it compare to BackgroundWorker?
Generalsilverlight support
Unruled Boy
18:25 17 Oct '08  
hi, there is no Thread.GetAvailableThreads and there Control.BeginInvoke out there in silverlight, could you please provide a silverlight version?

Regards,
unruledboy_at_gmail_dot_com
http://www.xnlab.com

Generalthanks for greate article
Member 3568631
3:10 3 Oct '08  
This code helps me call remoting functions with defineable timeout. And I have some suggestions to improve your code:
1) Sometimes wrapped code may throws exceptions and in some situations I need to know about this exceptions, so, I've added
internal Exception exception;
public Exception Exception
{ get { return exception; } }

into AsyncRes class and in main Do method adds res.exception = ex; right after
catch (Exception ex) { //we never want a rogue exception ... and modify AsyncRes.ReturnValue to
public object ReturnValue 
{
get
{
if (exception != null)
throw exception;
return retVal;
}
}

2) For calling function with timeout define two overloaded functions
public object EndDo()
{
return EndDo(Timeout.Infinite);
}

public object EndDo(int millisecondsTimeout)
{
if (AsyncWaitHandle.WaitOne(millisecondsTimeout, false))
return ReturnValue;
else
{
CancelOrAbort();
throw new TimeoutException(string.Format("Timeout was expired when calling {0} method", key));
}
}

GeneralRe: thanks for greate article
Nicholas Brookins
7:10 6 Oct '08  
Thank you, that's a good improvement. I'll add that to my list for the next update, and roll it in.
Generalexcellent article, tasty code
BillWoodruff
21:58 29 Sep '08  
Thanks, Nicholas !

delegate {vote(evaluation.five);}

best, Bill

"The greater the social and cultural distances between people, the more magical the light that can spring from their contact." Milan Kundera in Testaments Trahis

GeneralRe: excellent article, tasty code
Nicholas Brookins
3:12 30 Sep '08  
Thanks Bill, I appreciate it!
GeneralTypes
Seth Morris
17:55 29 Sep '08  
Nice class. I'm definitely making plans to use it.

For my preferred style, it would be good to see some more type safety and some more defensive programming added in a future version. I tried bolting some on but it's harder with a static class, since you can't add extension methods or make a subclass.

It would be nice to replace the naked bools with enums that have useful names. I've seen too many mistakes made where someone didn't remember what a parameter with a raw type meant, especially with overloads so similar.

Enums like

     public enum ReturnValue    {Get, Ignore};
public enum TryThreadPool {Yes, No};

would be nice.

Similarly, a generic for the state object might be nice (or might be too annoying, I haven't tried it).
AnswerRe: Types
Nicholas Brookins
18:30 29 Sep '08  
Hi Seth, thanks for the feedback - I'm glad this will be useful.

I like your idea of enums instead of the bool parameters - I'll keep that in mind for the next update. I tend to like flags, although I know some don't care for them. I'd be inclined to combine those both into a single flags enum like:


[Flags]
public enum AsyncOptions{
ReturnValue,
TryThreadPool,
Default,
//etc..
}


The advantage being that many future options could be added to the enum without affecting the method parameters.

I like generics but using a static class was more important to me for its simplicity, and to be honest I rarely use the state object. The state is most useful with a callback for completion, but as shown in another comment response here I usually build my notification into the anonymous method.
GeneralGreat idea, great explanation, great code... man great everything
Necromantici
17:02 29 Sep '08  
This is by far the best article I had saw in Code Project. It not only provides us with a great deal of well explained concepts but it also provides an excellent idea with sourcecode.

This article should definately win the best article of the year.

Thanks man for sharing and please keep the good work, I can't wait to see your next article...
GeneralRe: Great idea, great explanation, great code... man great everything
Nicholas Brookins
17:42 29 Sep '08  
Thank you for the kind comment! You're in luck, I have another article in the queue, I expect it will post by tomorrow - there will be a link to it here. I also have a couple more planned after that in the same 'Cleaner Code' series.
GeneralRe: Great idea, great explanation, great code... man great everything
Necromantici
18:26 29 Sep '08  
Thats great news. I'll be waiting for the next one and please keep the series because it looks promising...
QuestionNotification from the thread?
Ben Robbins
16:18 29 Sep '08  
Nice stuff Nicholas, but what do you do if you want the thread to asynchronously notify the calling thread when it is complete?

The IAsyncResult class allows the caller to check if the thread has completed, or wait for the thread to complete, but if we just want to get an event when the thread is complete then do we have to code that ourselves? That seems to be the most common scenario for us. We create a thread (from the main/GUI thread) and then want to be notified when it has completed, without having to block (and therefore making the application unresponsive). Does your code help in this scenario, and if so can you please give an example?

Thanks.
AnswerRe: Notification from the thread?
Nicholas Brookins
17:38 29 Sep '08  
Hi Ben,

Great question. The way I've handled that in a forms app is with a new feature of this class that is introduced in my next article; it hopefully will be approved and posted by tomorrow. It adds a method called UI that works similarly to this Do method, but always invokes onto a UI thread.

Thus I would do something like this:


//say this is in a button click event handler, we don't want to block.
Async.Do(delegate{
string s = RunLongReport(); //takes a while
Async.UI(delegate{ textBox1.Text = s}, textBox1);
};


The magic of anonymous methods makes that work very well for me - putting the extra call to UI at the end works like a notification, plus it is guaranteed to run on the right thread as opposed to a callback like much of the framework uses; you typically still have to invoke back on to the UI thread to do updates to controls. This also is concise in that you don't need to define a separate method for the notification, although you can if you like.

Check that article out, once it is posted there will be an update here with a link to it. If that doesn't fit your style, then I could easily add another overload of Do that took a second delegate as a callback that would run once the original had completed. Let me know what you think!

Thanks for the comment,

Nick
AnswerA little more information on the Reentrance option
Nicholas Brookins
7:38 26 Sep '08  
I think this needs a little more attention, as it is a very important feature of this class. The ReentranceMode enum has three options; Allow, Bypass, and Stack.

Allow: In this case, there are no locking checks. If you run a task, and then immediately run it again (possibly from a totally different area of your app) Allow will let them both go at the same time. Find if you aren't accessing any shared resources, say for logging or the like.

Stack: With stack mode, the class will detect if you already have a task executing in the same method (including anon. methods/delegates). It will block before the task begins, and continue once the prior copy completes. Good for shared resources like writing to a file, but be careful - if you get too many stacked up it could balloon your memory usage.

Bypass: The most common that I use, bypass will detect prior instances like stack does, but if it finds one already executing it dumps out immediately without running. This is good for things like internal checks or other tasks that need to run occasionally, but not necessarily back-to-back.
GeneralGreat stuff
jmw
6:32 26 Sep '08  
Excellent, now if only they'd integrate these features into the language!
Especially a foreach that has the option of running iterations in parallel and wait for them all to complete... you'd think in this day and age this type of thing would be far easier to use out of the box.

Thanks for posting!

John
GeneralRe: Great stuff
Nicholas Brookins
7:41 26 Sep '08  
Thanks!

Interesting you mention that. The Microsoft parallel team has a .NET extension library for Parallel Tasks that is in beta. It is a lot more far reaching than this, but is great for executing an action on all items in a list, etc.

Check it out here:

http://msdn.microsoft.com/en-us/concurrency/default.aspx[^]
GeneralGreat Idea
merlin981
5:01 26 Sep '08  
Great Idea! You did an excellent job explaining how to use your class (I was particularly pleased to see the anonymous method - so few developers demonstrate that). Five from me



~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LINQ Exchange - Learn about LINQ and Lambda Expressions
Mentally Jumbled - Geek Blog

Joke of the Day and Random Jokes - ReallyFunnyQuickJokes.com
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

GeneralRe: Great Idea
nbrookins
5:14 26 Sep '08  
Thanks, I appreciate it!
GeneralNice
Paul Brower
2:22 26 Sep '08  
Thanks for posting
GeneralRe: Nice
nbrookins
5:14 26 Sep '08  
No problem, thanks for reading!


Last Updated 1 Oct 2008 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010