I finally had a chance to install VS2010 Beta 1.Full instruction on how to here
VS2010 has undergone a major overhaul since VS 2008, a peek are some of the cool new eye candy can be found here
I've been waiting to get my hands on the TPL/PLINQ stuff and the Beta was definitely worth the wait. In a previous post we talked about using asynchronous actions for performing long running operations.
I was anxious to see how the new TPL stuff would help in achieving the same thing.
Interestingly enough, the TPL API is based on Actions as well, allowing both the generic and non-generic versions to be used for creating a Task(also supports Funcs).
The Thread pool has undergone some major rework, especially the scheduling algorithm is much more smarter now than the current implementation in.NET 3.5
The unit of abstraction is a Task as opposed to a Thread, so let's dive in and see how this looks and feels as opposed to the abstraction we previously built for asynchronous Actions. In the simplest form, the equivalent declaration for creating a Task that can execute an Action asynchronously looks like so:
- public static void SomeMethod()
- {
-
-
- Console.WriteLine("In some method");
-
- }
-
- static void Main(string[] args)
- {
- var task = Task.Factory.StartNew(SomeMethod);
- task.Wait();
- }
You start a task and Wait for it to complete. Lets create a bunch of these tasks to execute on the GetUsers(), GetCountries() and GetLanguages() method from the previous post.
- Task[] tasks = new Task[]
- {
- Task.Factory.StartNew(() => countries = GetCountries()),
- Task.Factory.StartNew(() => countries = GetUsers()),
- Task.Factory.StartNew(() => countries = GetLanguages())
- };
- Task.WaitAll(tasks);
This looks fairly similar from a usage persepctive to what we had for executing asynchronous actions
- List<Action> actions = new List<Action>()
- {
- () => countries = GetCountries(),
- () => users = GetUsers(),
- () => languages = GetLanguages(),
- };
- actions.ExecAsync();
One obviously noticeable difference in using the Task is that you use the Factory method StartNew to create and execute the task at the point of declaration, and what you get back is a handle to the executing Task. So the Task[] contains a handle to all the executing tasks.
The ExecAsync extension method on the other hand is passed a collection of Actions to execute. I find the idea of being able to create and execute the Task in one statement very useful and terse.
Let's now compare the performance of executing Actions using Tasks v/s executing Actions using The ExecAsync()
- public static class Extensions
- {
- public static void ExecAsync(this IEnumerable<Action> actions)
- {
- int count = 0;
- AutoResetEvent[] events = new AutoResetEvent[actions.Count()];
- IAsyncResult[] results = new IAsyncResult[actions.Count()];
- for (int i = 0; i < events.Length; i++)
- {
- events[i] = new AutoResetEvent(false);
- }
-
- foreach (var action in actions)
- {
- int localCount = count;
- results[count++] =
- action.BeginInvoke((r) =>
- {
- try
- {
- if (r.IsCompleted)
- {
- Action act = r.AsyncState as Action;
- act.EndInvoke(results[localCount]);
- }
- }
- finally
- {
-
-
- events[localCount].Set();
- }
- }, action);
- }
- WaitHandle.WaitAll(events);
- }
- }
-
- public class Program
- {
- public static string[] GetUsers()
- {
-
- Thread.SpinWait(3000);
- Console.WriteLine("Current Thread:{0}", Thread.CurrentThread.ManagedThreadId);
- return new[] { "Jack", "Jon", "Jim" };
- }
-
- public static string[] GetCountries()
- {
-
- Thread.SpinWait(3000);
- Console.WriteLine("Current Thread:{0}", Thread.CurrentThread.ManagedThreadId);
- return new[] { "US", "UK", "Canada" };
- }
-
- public static string[] GetLanguages()
- {
-
- Thread.SpinWait(3000);
- Console.WriteLine("Current Thread:{0}", Thread.CurrentThread.ManagedThreadId);
- return new[] { "English", "French", "German" };
- }
- static void Main(string[] args)
- {
-
- string[] countries;
- string[] users;
- string[] languages;
- List<Action> actions = new List<Action>()
- {
- () => countries = GetCountries(),
- () => users = GetUsers(),
- () => languages = GetLanguages(),
- };
- Console.WriteLine("Running Actions Asynchronously");
- Console.WriteLine("----------------------");
- Stopwatch watch = Stopwatch.StartNew();
- actions.ExecAsync();
- watch.Stop();
- Console.WriteLine("Total time taken:{0} milliseconds", watch.ElapsedMilliseconds);
-
- Console.WriteLine();
- Console.WriteLine("Running Actions using Tasks");
- Console.WriteLine("----------------------");
- watch.Reset();
- watch.Start();
- Task[] tasks = new Task[]
- {
- Task.Factory.StartNew(() => countries = GetCountries()),
- Task.Factory.StartNew(() => countries = GetUsers()),
- Task.Factory.StartNew(() => countries = GetLanguages())
- };
- Task.WaitAll(tasks);
- watch.Stop();
- Console.WriteLine("Total time taken:{0} milliseconds", watch.ElapsedMilliseconds);
- }
- }
Let's give it a spin and see the numbers:

The version using Tasks is almost 5 times faster! which suggests that TPL is doing a far superior job under the covers in effectively scheduling and executing the Actions.
TPL has a host of goodness which I've barely started exploring.Among other goodies, is the ability to have parent-child relationships between tasks and support for cancellation using a CancellationToken.
I hope to do more frequent posts especially around TPL since this has piqued my interest.
As always happy coding!
