The .NET 4.0 Task API returning results to calling thread

Continuing with the theme of long running operations, in this post I'll take a look at how to use the new Task API in .NET 4.0 to easily perform a long running operation in the background and return the results back on the thread that created the Task. Since this wasn't immediately obvious to me when reading the documentation, I figured it might be of some help to others.

When we create a new Task, we are telling the framework that we have a piece of work that needs to be completed. We aren't specifying how that work should be completed, as that is the job of the TaskScheduler. If we want to be notified when a Task completes, we can pass a delegate to the Task via the ContinueWith method like so:

Task t = Task.Factory.StartNew(LongRunningOperation);  
t.ContinueWith(OperationCompletedCallback);  
t.Start();  

Since the ContinueWith method is actually constructing a new Task, it too will use a TaskScheduler to determine how it is run. The default TaskScheduler uses the ThreadPool to parallelize the Tasks, so both our original task and our callback function will be run in ThreadPool worker threads.

In order to get the completion Task to run on the main thread, the trick is to pass it a different TaskScheduler. This is accomplished through the TaskScheduler.FromCurrentSynchronizationContext static method. We have to use an overload of the ContinueWith method to pass in a scheduler:

Task t = Task.Factory.StartNew(LongRunningOperation);  
t.ContinueWith(OperationCompletedCallback,  
    TaskScheduler.FromCurrentSynchronizationContext());
t.Start();  

If you are calling this Task from a thread where System.Threading.SynchronizationContext.Current returns a valid context, the ContinueWith method will be called on the thread that started the Task. If you are running within the context of WPF or WinForms, you shouldn't have to worry about this not being valid.

I did run into a problem when using MAF (the whole System.AddIn stuff) where the SynchronizationContext was null for my addins. It was easy enough to create one:

DispatcherSynchronizationContext context =  
    new DispatcherSynchronizationContext(Dispatcher.CurrentDispatcher);

-AH

Creating a Busy Indicator in a separate thread in WPF

One of the worst sins a GUI developer can commit is blocking the UI thread. Unfortunately, there will always be times where we must do certain operations on the UI thread. For instance, creating the ItemContainers in an ItemsControl when binding to a very large collection (I know with virtualization... Read More