The show must go on
In previous posts, we’ve talked about PLINQ and the Parallel class - two features that make parallel data execution much easier. But if someone asked me what’s the coolest feature in the Task Parallel Library, I’d have a clear answer: handling asynchronous tasks with the Task class.
For years, developers have had to wrestle - okay, let’s be honest, fight - with multitasking and asynchronous code execution. Since the early versions of C#, we could manually create threads using the Thread class, but the process was far from simple and had this annoying complexity when handling cancellations or updating the UI. And while there have been attempts at improvement like the IAsyncResult interface, we’ve also seen horrible abominations like the damn BackgroundWorker, which is about as pleasant as performing brain surgery on yourself while wearing oven mitts.
Task to the rescue
So when TPL and the Task class appeared, developers finally found a simple and convenient way to run asynchronous code in parallel. This is really important because nowadays, in many applications (at least the well-designed ones), tasks run in parallel to access external or ‘expensive’ resources, databases, and especially for UI updates. It’s so important that it’s going to be one of the biggest improvements in the upcoming C# 5.0 version, which we’ll talk about at the end of this series.
Actions everywhere
Just like the static Parallel class and most of TPL, the Task class is based on actions. So if you’re not too familiar with them, check out the post about actions I published a while back on the topic.
In its most basic syntax, you can use it like this:
var t = new Task(() => {
Thread.Sleep(1000);
Console.WriteLine("A");
});
t.Start();
Console.WriteLine("B");
Or what’s the same thing:
Task.Factory.StartNew(() => {
Thread.Sleep(1000);
Console.WriteLine("A");
});
Console.WriteLine("B");
The only difference is that in the first one, we declare the variable specifying the action to execute and then explicitly run it using the Start method. In the second one, we don’t use any variable - we just specify and execute the action using the StartNew method from the Task.Factory class.
Looking at the code above, what do you think gets written first? A or B? Obviously B, since we can imagine how the code doesn’t stop at Thread.Sleep(1000) (this runs on another thread), so it immediately executes the ‘B’ print. Correct.
But tasks are much more than this. They allow everything from returning results to managing these work units by chaining tasks after others, waiting for one or a group of tasks to finish before executing another, canceling a task, or propagating exceptions between them.
1) Returning a value from a task…
Just like a method, a task can return anything from a basic type (int, string) to any complex type. To make the example more interesting, let’s use a method that scans the local network for SQL Server instances and returns a list of strings with their names (server1, server2, etc.):
public static List<string> GetSQLServerNames()
{
List<string> sqlservernames = new List<string>();
sqlservernames.Add("local");
SqlDataSourceEnumerator enumSQLServers = SqlDataSourceEnumerator.Instance;
foreach (DataRow server in enumSQLServers.GetDataSources().Rows)
{
if (server["InstanceName"] is System.DBNull)
sqlservernames.Add(server["ServerName"].ToString());
else
sqlservernames.Add(
string.Format("{0}\\{1}", server["ServerName"], server["InstanceName"]));
}
Console.WriteLine("end get servers");
return sqlservernames;
}
The advantage of using this method is that it takes a few seconds to execute, making it a perfect candidate for asynchronous execution:
var getServersMethodAsync = new Task<List<string>>(() => GetSQLServerNames());
getServersMethodAsync.Start();
Console.WriteLine("end call");
2) …and then continuing with another task
Now let’s say we have another method that handles updating the UI based on the previous list:
private void UpdateServersListUI(List<string> servers)
{
comboBox1.Items.Clear();
servers.ForEach(p => comboBox1.Items.Add(p));
}
Wouldn’t it make sense for this to happen when the previous task finishes? Well, absolutely, and chaining tasks is trivial and gives developers a lot of power. I’m sure you can already think of some applications for this ;)
Chaining tasks is as simple as using the ‘ContinueWith’ method:
var getServersMethodAsync = new Task<List<string>>(() => GetSQLServerNames());
getServersMethodAsync.Start();
Console.WriteLine("end button2");
getServersMethodAsync.ContinueWith((p) => UpdateServersListUI(getServersAsync.Result));
On paper, this should work, but it won’t, because we haven’t noticed one detail: In the .NET platform, it’s not possible to update a control from a thread different from the one that created it, and the entire UI is created on the main thread.
Setting the execution context
Before TPL, to update the UI from another thread, we had to use the ‘Invoke’ method from the ‘Control’ class. So we’d need to modify the previous method so that the Invoke call creates an action with the code to execute on the main thread:
private void UpdateServersListUI(List<string> servers)
{
if (this.InvokeRequired) this.Invoke(new Action(() =>
{
comboBox1.Items.Clear();
servers.ForEach(p => comboBox1.Items.Add(p));
}));
}
Fortunately, this won’t be necessary because as part of TPL’s magic, we get the ability to call ‘TaskScheduler.FromCurrentSynchronizationContext’, which allows us to access the UI safely. So all we need to do is modify the previous code to chain the second task using the synchronization context mentioned above and forget about the Invoke call:
getServersMethodAsync.ContinueWith((p) => UpdateServersListUI(getServersAsync.Result),
TaskScheduler.FromCurrentSynchronizationContext());
Waiting for multiple tasks to complete
Another really interesting feature is the ability to wait for an entire group of tasks to finish, or just one of them. Let’s say we have a series of tasks that apply effects to a series of images:
var t1 = Task.Factory.StartNew(
() => pictureBox1.Image = Properties.Resources.Landscape08.Invert());
var t2 = Task.Factory.StartNew(
() => pictureBox2.Image = Properties.Resources.Landscape08.Grayscale());
var t3 = Task.Factory.StartNew(
() => pictureBox3.Image = Properties.Resources.Landscape08.Brightness(140));
var t4 = Task.Factory.StartNew(
() => pictureBox4.Image = Properties.Resources.Landscape08.Contrast(80));
var t5 = Task.Factory.StartNew(
() => pictureBox5.Image = Properties.Resources.Landscape08.Gamma(1, 5, 1));
var t6 = Task.Factory.StartNew(
() => pictureBox6.Image = Properties.Resources.Landscape08.Color(255, 0, 0));
It might be interesting not to continue executing code until the first one finishes, or until they all finish, or until they all finish but with a timeout. That is, if they don’t all finish in 100 milliseconds, continue with execution:
Task.WaitAny(new Task[] {t1, t2, t3, t4, t5, t6});
Task.WaitAll(new Task[] {t1, t2, t3, t4, t5, t6});
Task.WaitAll(new Task[] {t1, t2, t3, t4, t5, t6}, 100);
Another way to achieve this is by creating a task, specifying that it should wait for either some task or all specified tasks to complete:
var t7 = Task.Factory.ContinueWhenAll(new[] { t1, t2, t3, t4, t5, t6 }, (t) =>
{
//DoSomething...
});
Canceling tasks
Just like we’ve seen in previous posts in this series, the Task class also supports cancellations. In my opinion, these are more commonly used than their equivalents in PLINQ or Parallel, since they can allow a user to cancel access to a resource that’s taking longer than expected (like a URL) and the tasks that were supposed to execute afterwards.
Starting with a method that does long work:
private void DoALongWork(CancellationTokenSource cs)
{
try
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
cs.Token.ThrowIfCancellationRequested();
}
}
catch (OperationCanceledException ex)
{
Console.WriteLine(ex.Message);
}
}
We can cancel the task by calling the token’s ‘Cancel’ method:
var cs = new CancellationTokenSource();
var t = Task.Factory.StartNew(
() => DoALongWork(cs)
);
Thread.Sleep(500);
cs.Cancel();
When canceled (cs.Cancel), the code will enter the catch block of the ‘DoALongWork’ method where the appropriate cancellation actions will be performed.
Cancellation and state
Finally, in some cases it might be interesting that if the task succeeds, it calls a second one, but if it gets canceled, it calls a third one. To do this, we need to check the state of the first task and perform a little trick: the exception must not be caught in a try block!
So the method code would look like this (without try):
private void DoALongWork(CancellationTokenSource cs)
{
for (int i = 0; i < 100; i++)
{
Thread.Sleep(10);
cs.Token.ThrowIfCancellationRequested();
}
}
And the call would be like this:
var cs = new CancellationTokenSource();
var t = Task.Factory.StartNew(
() => DoALongWork(cs)
);
Thread.Sleep(500);
cs.Cancel();
t.ContinueWith(task =>
{
switch (task.Status)
{
case TaskStatus.Faulted:
MessageBox.Show("Fail");
break;
case TaskStatus.RanToCompletion:
MessageBox.Show("Success");
break;
}
}, TaskScheduler.FromCurrentSynchronizationContext());
As you can see, we can change the code flow based on the task’s state.
Important: To run this last example, you need to execute without debugging (Ctrl+F5)
Well, we’ve left some things out, but to keep this post from getting too long, we’ll see them later in other advanced posts in the same series.
Until then, I promise to update the series more frequently than I have so far ;)

Resistance is futile.