Related Posts:
- What’s new in Beta 1 for the Task Parallel Library? (Part 1/3)
- What’s new in Beta 1 for the Task Parallel Library? (Part 3/3)
Last week we talked about changes under the covers, redesigns in System.Threading.Parallel, and using CancellationTokens. So what else is new in TPL for Beta 1? In this post, we’ll cover the new TaskFactory class, the plight of Future<T> (Task<TResult>), and TaskCompletionSource<TResult>.
TaskFactory
There are two ways to create and schedule Tasks: use constructors and instance methods, or use static methods like StartNew. In previous releases, these two functionalities were jammed into the Task class, and we realized that separating them would result in a cleaner design. So we introduced the TaskFactory class, which contains all static methods that create and/or schedule Tasks. In Beta 1, these static methods include more than just StartNew, but those will be covered in the next post!
For convenience, Task now contains a static Factory property which returns a default instance of TaskFactory. So for many users, the biggest practical difference is that where you would have used Task.StartNew (or Task.Create in the June 2008 CTP and earlier), you now use:
Task.Factory.StartNew(() =>
{
…
});
An additional advantage of TaskFactory is the ability to consolidate the specifying of options such as creation options, continuation options, and which scheduler to use:
TaskFactory myFactory = new TaskFactory(
myScheduler, myCreationOptions, myContinuationOptions);
Task t0 = myFactory.StartNew(() => { });
Task t1 = myFactory.StartNew(() => { });
Task t2 = myFactory.StartNew(() => { });
Task<TResult>
In previous releases, Tasks that produced results were called Futures. Moving forward, we’ve opted to rename Future<T> to Task<TResult>, a type that derives from Task. There were a number of reasons, including:
· We constantly found ourselves describing Futures as “tasks that return results”.
· Many folks, that we spoke with, found the name Task<TResult> clearer.
· There is some discrepancy in literature and concurrency circles about exactly what’s implied by the term “future” (What functionality should or should not be exposed? Side-effect free?)
· The name Future has a not-related-to-Task implication. However, we wanted Task<TResult> to derive from Task so that the former could be treated polymorphically as the latter.
· We wanted TaskFactory methods to be able to return “tasks that return results”. For example, it would have been odd for Task.Factory.StartNew to return a Future.
TaskCompletionSource<TResult>
A Task<TResult> is typically used to asynchronously execute a delegate that computes and returns a result. Sometimes however, the asynchronous operation cannot be represented by a delegate, but rather is performed by an external entity.
Such functionality was originally supported on Future<T>, via a special constructor that did not take a delegate. A Future created this way could have its Value or Exception properties set using the respective setters. There were several problems with this approach, for example:
· The setters were only usable if the Future<T> was created with the special constructor.
· Commonly, a producer would want to hand out a Future<T> to consumers, but not allow the consumers to resolve it. However, in this scheme, anyone who had a reference to the Future<T> could resolve it.
We’ve addressed these issues by introducing a new TaskCompletionSource<TResult> type:
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
Task<int> task = tcs.Task;
// Sometime later…
tcs.SetResult(computedResult); // Or tcs.SetException(exc), or tcs.SetCanceled()
// In a consumer elsewhere…
try { Console.WriteLine(task.Result); }
catch (AggregateException ae) { }
In this post, we covered the most fundamental redesigns to TPL tasks. “What’s new in Beta 1 for TPL (Part 3/3)” will cover all remaining changes, such as other helper methods on TaskFactory and the new TaskScheduler. Look forward to it!
0 comments