TaskScheduler.Current and TaskScheduler.FromCurrentSynchronizationContext() difference?

Dahevos picture Dahevos · Apr 16, 2013 · Viewed 7.8k times · Source

I have a task to get products from database, and the ContinueWith action that operate some UI modification, therefore I had a problem because the Task create a new thread, and the UI modification was executed not in the UI Thread.

I tried to use this fix :

var currentScheduler = TaskScheduler.Current;

Task.Factory.StartNew(() =>
{    
    // get products   
}).ContinueWith((x) => handleProductsArrived(x.Result, x.Exception), currentScheduler);

but it didn't work at all. I check and the ContinueWith was not executed in the thread from currentScheduler but in an another.

I discovered this method :

Task.Factory.StartNew(() =>
{
    // get products
}).ContinueWith((x) => handleProductsArrived(x.Result, x.Exception), TaskScheduler.FromCurrentSynchronizationContext());

and it works. So what's the differences? Why didn't my first code work? Thanks!

Answer

Jon Skeet picture Jon Skeet · Apr 16, 2013

From the documentation for TaskScheduler.Current:

When not called from within a task, Current will return the Default scheduler.

Then from the Task Schedulers documentation:

The default scheduler for Task Parallel Library and PLINQ uses the .NET Framework ThreadPool to queue and execute work.

So if you use TaskScheduler.Current when you're not in a task, you'll get a scheduler which uses the thread pool.

If you call TaskScheduler.FromCurrentSynchronizationContext(), you'll get one for the current synchronization context - which in Windows Forms or WPF (when called from a UI thread) is a context which schedules work on the relevant UI thread.

So that's why the first code didn't work: it executed your continuation on a thread pool thread. Your second code executed the continuation on the UI thread.

Note that if you can use C# 5 and async/await, all of this is handled much more simply.