Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders posts can be found here.
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 like Parallel.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
Read more: James Michael Hare
QR: