Parallel Programming with Microsoft .Net : Futures - The Basics

2/19/2011 5:10:06 PM
In other words, asynchronous operations often act like functions. Of course, tasks can also do other things, such as reordering values in an array, but calculating new values is common enough to warrant a pattern tailored to it. It’s also much easier to reason about pure functions, which don’t have side effects and therefore exist purely for their results. This simplicity becomes very useful as the number of cores becomes large.

1. Futures

The following example is from the body of a sequential method.

var b = F1(a);
var c = F2(a);
var d = F3(c);
var f = F4(b, d);
return f;

Suppose that F1, F2, F3, and F4 are processor-intensive functions that communicate with one another using arguments and return values instead of reading and updating shared state variables.

Suppose, also, that you want to distribute the work of these functions across available cores, and you want your code to run correctly no matter how many cores are available. When you look at the inputs and outputs, you can see that F1 can run in parallel with F2 and F3 but that F3 can’t start until after F2 finishes. How do you know this? The possible orderings become apparent when you visualize the function calls as a graph. Figure 1 illustrates this.

Figure 1. A task graph for calculating f

The nodes of the graph are the functions F1, F2, F3, and F4. The incoming arrows for each node are the inputs required by the function, and the outgoing arrows are values calculated by each function. It’s easy to see that F1 and F2 can run at the same time but that F3 must follow F2.

Here’s an example that shows how to create futures for this example. For simplicity, the code assumes that the values being calculated are integers and that the value of variable a has already been supplied, perhaps as an argument to the current method.


The Result property returns a precalculated value immediately or waits until the value becomes available.

Task<int> futureB = Task.Factory.StartNew<int>(() => F1(a));
int c = F2(a);
int d = F3(c);
int f = F4(futureB.Result, d);
return f;

This code creates a future that begins to asynchronously calculate the value of F1(a). On a multicore system, F1 will be able to run in parallel with the current thread. This means that F2 can begin executing without waiting for F1. The function F4 will execute as soon as the data it needs becomes available. It doesn’t matter whether F1 or F3 finishes first, because the results of both functions are required before F4 can be invoked. (Recall that the Result property does not return until the future’s value is available.) Note that the calls to F2, F3, and F4 do not need to be wrapped inside of a future because a single additional asynchronous operation is all that is needed to take advantage of the parallelism of this example.

Of course, you could equivalently have put F2 and F3 inside of a future, as shown here.

Task<int> futureD = Task.Factory.StartNew<int>(
() => F3(F2(a)));
int b = F1(a);
int f = F4(b, futureD.Result);
return f;

It doesn’t matter which branch of the task graph shown in the figure runs asynchronously.

An important point of this example is that exceptions that occur during the execution of a future are thrown by the Result property. This makes exception handling easy, even in cases with many futures and complex chains of continuation tasks. You can think of futures as either returning a result or throwing an exception. Conceptually, this is very similar to the way any .NET function works. Here is an example.


Futures, as implemented by the Task<TResult> class, defer exceptions until the Result property is read.

Task<int> futureD = Task.Factory.StartNew<int>(
() => F3(F2(a)));
int b = F1(a);
int f = F4(b, futureD.Result);
return f;
catch (MyException)
Console.WriteLine("Saw MyException exception");
return -1;

If an exception of type MyException were thrown in F2 or F3, it would be deferred and rethrown when the Result property of futureD is read. Getting the value of the Result property occurs within a try block, which means that the exception can be handled in the corresponding catch block.

2. Continuation Tasks

It’s very common for one asynchronous operation to invoke a second asynchronous operation and pass data to it. Continuation tasks make the dependencies among futures apparent to the run-time environment that is responsible for scheduling them. This helps to allocate work efficiently among cores.

For example, if you want to update the user interface (UI) with the result produced by the function F4 from the previous section, you can use the following code.

TextBox myTextBox = ...;
var futureB = Task.Factory.StartNew<int>(() => F1(a));
var futureD = Task.Factory.StartNew<int>(() => F3(F2(a)));
var futureF = Task.Factory.ContinueWhenAll<int, int>(
new[] { futureB, futureD },
(tasks) => F4(futureB.Result, futureD.Result));
futureF.ContinueWith((t) =>
(Action)(() => { myTextBox.Text = t.Result.ToString(); }))

This code structures the computation into four tasks. The system understands the ordering dependencies between continuation tasks and their antecedents. It makes sure that the continuation tasks will start only after their antecedent tasks complete.

The first task, futureB, calculates the value of b. The second, futureD, task calculates the value of d. These two tasks can run in parallel. The third task, futureF, calculates the value of f. It can run only after the first two tasks are complete. Finally, the fourth task takes the value calculated by F4 and updates a text box on the user interface.

The ContinueWith method creates a continuation task with a single antecedent. The ContinueWhenAll<TAntecedentResult, TResult> method of the Task.Factory object allows you to create a continuation task that depends on more than one antecedent task.

  •  Using Non-Windows Systems to Access Exchange Server 2010 : Outlook Express
  •  Using Non-Windows Systems to Access Exchange Server 2010 : Understanding Non-Windows–Based Mail Client Options
  •  Deploying the Client for Microsoft Exchange Server 2010 : Deploying with Microsoft System Center Configuration Manager 2007
  •  Deploying the Client for Microsoft Exchange Server 2010 : Pushing Outlook Client Software with Group Policies
  •  Deploying the Client for Microsoft Exchange Server 2010 : Installing the Outlook Client for Exchange Server
  •  Deploying the Client for Microsoft Exchange Server 2010 : Preparing the Deployment
  •  Parallel Programming with Microsoft .Net : Parallel Aggregation - Design Notes
  •  Parallel Programming with Microsoft .Net : Parallel Aggregation - Variations
  •  Leveraging and Optimizing Search in SharePoint 2010 : Uninstalling FAST Search Server 2010 for SharePoint
  •  Leveraging and Optimizing Search in SharePoint 2010 : Customizing the FAST Search User Interface
  •  Deploying the Client for Microsoft Exchange Server 2010 : Planning Considerations and Best Practices
  •  Deploying the Client for Microsoft Exchange Server 2010 : Understanding Deployment Options
  •  Deploying the Client for Microsoft Exchange Server 2010 : Outlook 2007 Auto Account Setup
  •  Leveraging and Optimizing Search in SharePoint 2010 : Deploying FAST Search Service Applications
  •  Leveraging and Optimizing Search in SharePoint 2010 : Customizing the Search User Interface
  •  Leveraging and Optimizing Search in SharePoint 2010 : Keywords and Best Bets
  •  Leveraging and Optimizing Search in SharePoint 2010 : Federating Search
  •  Leveraging and Optimizing Search in SharePoint 2010 : Search Scopes
  •  Active Directory Domain Services 2008 : View Cached Credentials on a Read-Only Domain Controller
  •  Active Directory Domain Services 2008 : Remove a User, Group, or Computer from the Password Replication Policy
    Most View
    Amateur Astronomy Applications (Part 1) - WorldWide Telescope, Stellarium
    Ubuntu Arrives For Touch Phones (Part 2)
    Simplicity and Precision: Test Planning In Agile Projects
    Silverlight : Data Binding - Validating Input for Bound Data
    Samsung Galaxy SIII Mini - A Small Galaxy Having Few Stars (Part 1)
    Burn-In Your CPU With Linux (Part 2)
    Sound Blaster Tactic 3D Rage
    Ubuntu Phones To Hit Stores In October (Part 1)
    Asus PadFone - A 3-In-1 Solution For You
    Calendar Plus - Simple And Friendly
    Top 10
    Gigabyte Z77- D3H - An Intriguing Motherboard
    Intel DZ77GA–70K Motherboard - Reliable And Decent Performance
    Motherboard Buyer’s Guide - A Home For Haswell (Part 2) : Gigabyte G1.Sniper 5, Gigabyte Z87X-UD3H
    Motherboard Buyer’s Guide - A Home For Haswell (Part 1) : ASRock Z87 Extreme4, ASUS Maximus VI Extreme, ASUS Maximus VI Hero
    Mouse And Keyboard Buyer’s Guide - Dual Wielding (Part 3)
    Mouse And Keyboard Buyer’s Guide - Dual Wielding (Part 2)
    Mouse And Keyboard Buyer’s Guide - Dual Wielding (Part 1)
    MSI Z77A-G41 Plus - A Basic Motherboard
    SilverStone Raven RV04 – A Traditional Case
    Samsung Galaxy Note 8 - Ever Expanding Options For Samsung Fans