14

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!

1 Answer 1

23

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.

Sign up to request clarification or add additional context in comments.

6 Comments

This is useful when you can't use async/await like in a constructor of a UI component, right?
@Chin: I wouldn't particularly use it for that - I'd generally have a static async method which does the work and then calls a quick constructor.
Can you elaborate a bit more on that? Particularly I have another question here: stackoverflow.com/questions/31886276/… that is kinda related to that
@Chin Constructor(){ DoHeavyWorkAndUpdate(); } private async void DoHeavyWorkAndUpdate(){ await Task.Run(()=>{... /*compute here, runs on background thread*/ }); /*this runs on the main thread*/ UpdateUI(); }
@JonSkeet What would be the "much more simple" solution? I would really like to use that. My problem: I have a base class, that will be used heavily by other classes. I would like to initialize some properties only after every inherited class constructor was executed. So creating a task on the current synccontext is what I came up. I feel like async await should be able to solve this, but I couldn't figure out.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.