Many times in software development, we want to invoke several processes at one time and continue when we get all the results back.  Obviously, if we were needing to process a sequence of items in a similar matter, we could use PLINQ.  Unfortunately, when the things we want to invoke asynchronously are heterogeneous tasks, PLINQ doesn’t really fit the bill.  Today we will look at a handy method in the Parallel class that can help us accomplish this.

Invoking processes asynchronously

Let’s say we have three completely separate methods and we want to invoke all three of them and the continue when they have returned.  These methods may be from separate classes and have completely different parameters and/or return types.  Thus, it may not be possible to use facilities likeParallel.ForEach() or PLINQ.

So how would we do this?  Well, we could obviously create three threads, start them, and join on them to come back:

   1: var threads = new List<Thread>
   2:                   {
   3:                       new Thread(SomeMethod),
   4:                       new Thread(() => SomeOtherMethod(x, y)),
   5:                       new Thread(() => { result = YetAnotherMethod(x, y, z); })
   6:                   };
   7: threads.ForEach(t => t.Start());
   8: threads.ForEach(t => t.Join());

Which, as you can see, can be used to call any method that fits Thread’s constructor (or can be adapted to it using a lambda, etc.). 

But that’s a bit heavy.  In addition, if an unhandled exception is thrown in one of those methods it will kill the thread, but we don’t have a very clean way of catching it here at the point of invocation.

Of course, we also have the Task class from the TPL which can help simplify threads:

   1: var tasks = new []
   2:
                 {
   3:                     Task.Factory.StartNew(SomeMethod),
   4:                     Task.Factory.StartNew(() => SomeOtherMethod(x, y)),
   5:                     Task.Factory.StartNew(() => { result = YetAnotherMethod(x, y, z); })
   6:                 };
   7: Task.WaitAll(tasks);

This simplifies things on the surface, and better yet simplifies things a lot more with exception handling as well (TPL task exceptions get wrapped into a AggregateException and can be caught at the WaitAll() – and other possible places).

But there’s an even easier way…

Parallel.Invoke() – Easily invoke asynchronously

Just as the TPL gave us Parallel.For() and Parallel.ForEach() to make processing loops asynchronously a breeze, it also gave us Parallel.Invoke(), which takes any number of Action delegates and invokes them asynchronously and waits for them to rejoin.  If your methods have return values you want to store or need parameters, you can easily use lambda expressions as well:

   1: Parallel.Invoke(
   2:
     SomeMethod,
   3:     () => SomeOtherMethod(x, y)),
   4:     () => { result = YetAnotherMethod(x, y, z); })

As long as the method can convert to an Action or you can form a lambda that will allow it to do so, you can use the Parallel.Invoke() to easily call them all in parallel without having to worry about starting or joining the threads or tasks.  And, because this is part of the TPL, you get the nice AggregateException handling as well that lets you easily catch tasks thrown from the underlying tasks.

Summary

When you just need to fire off several methods in parallel, consider the Parallel.Invoke().  It is a very handy way to invoke several different methods easily without having to worry about all the details of creating tasks or threads.