What’s new in Beta 1 for the Task Parallel Library? (Part 3/3)

Related posts:

So what else is new in TPL for Beta 1 (finally)?  In the last post, we mentioned that TaskFactory offers more static helper methods than just StartNew.  In this post, we’ll cover those methods (FromAsync, ContinueWhenAll, and ContinueWhenAny) as well as the new TaskScheduler class.

FromAsync

In order to better to integrate the Asynchronous Programming Model (APM) with TPL, we added FromAsync to TaskFactory.  Here’s an example for reading asynchronously from a FileStream:

FileStream fs = …;

byte [] buffer = …;

Task<int> t = Task<int>.Factory.FromAsync(

    fs.BeginRead, fs.EndRead, buffer, 0, buffer.Length, null);

 

The overloads provided for FromAsync support all implementations of the APM pattern up to a certain number of parameters (which represent the vast majority of the APM implementations in the .NET Framework); additional overloads that work directly with IAsyncResults are also available to cover the stragglers.

FromAsync returns a Task that represents the asynchronous operation, and once you have that Task, you have all of the functionality available to normal Tasks.  You can wait on them, schedule continuations off of them, and so on.

ContinueWhenAll and ContinueWhenAny

The ContinueWith method on Task enables a slew of powerful patterns and is a fundamental building block in many higher-level implementations.  However, while being able to run a task when another completes is useful, it’s also useful to be able to run a task when any or all of a set of tasks completes.  For these purposes, we added ContinueWhenAll and ContinueWhenAny to TaskFactory:

var tasks = new Queue<Task>();

for (int i = 0; i < N; i++)

{

    tasks.Enqueue(Task.Factory.StartNew(() =>

    {

       

    }));

}

 

// Schedule a task to execute when all antecedent tasks complete.

var continuationOnAll = Task.Factory.ContinueWhenAll(

    tasks.ToArray(), (Task[] completedTasks) =>

    {

       

    });

 

// Schedule a task to execute when one (any) antedent task completes.

var continuationOnAny = Task.Factory.ContinueWhenAny(

    tasks.ToArray(), (Task completedTask) =>

    {

       

    });

 

TaskScheduler

In previous releases, you may have seen a TaskManager class.  TaskManager is no more – replaced by the new TaskScheduler class.  By default, tasks get scheduled on TaskScheduler.Default, which is an implementation based on new work-stealing queues in the .NET 4 ThreadPool (as mentioned in the first post).  However, TaskScheduler is an abstract class, so developers can derive from it and create their own custom schedulers!  The default scheduler should be sufficient in most cases, but a custom scheduler might be needed for some special scenarios (strict FIFO scheduling, special priorities, etc).

One of those special scenarios, scheduling onto the UI thread, is supported out-of-the-box.  The TaskScheduler.FromCurrentSynchronizationContext method returns a scheduler that wraps the current synchronization context.  Thus, if it is called on the GUI thread of a Windows Forms application, you’ll get back a TaskScheduler that marshals any queued tasks to the GUI thread.  Here’s an example:

public void Button1_Click(…)

{

    var ui = TaskScheduler.FromCurrentSynchronizationContext();

 

    Task.Factory.StartNew(() =>

    {

        return LoadAndProcessImage(); // compute the image

    }).ContinueWith(t =>

    {

        pictureBox1.Image = t.Result; // display it

    }, ui);

}

 

In this set of posts, we talked about most of the major updates to TPL for Beta 1, including changes under the covers, renames of some of our core types, redesigns of some of our core functionalities, and the addition of brand new types.  Some topics were left out such as debugger and profiler integration.  Stay tuned for posts on those and thanks for reading!