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!
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.