FAQ :: StartNew() with TaskScheduler.FromCurrentSynchronizationContext() doesn’t work?

Danny Shih

We’ve seen a number of folks write the following code to execute on the UI thread and get unexpected behavior.


TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();

Task uiTask = Task.Factory.StartNew(delegate
    // … Update UI component; BUG!
}, uiScheduler);

The issue is that the StartNew call will bind to the following overload…


public Task StartNew(Action<Object> action, Object state);

…because the following overload does not exist!


public Task StartNew(Action action, TaskScheduler scheduler);


As a result, uiScheduler just becomes the AsyncState for uiTask instead of being the TaskScheduler on which it executes, so the UI updates will end up running on the ambient scheduler captured during the StartNew call (usually not desired).  Once the issue is identified, it’s easy to pick the right overload:


Task t = Task.Factory.StartNew(delegate
    // … Update UI component
}, CancellationToken.None, TaskCreationOptions.None, uiScheduler);


Note that you won’t run into the same confusion with ContinueWith (the API that’s more commonly used with TaskScheduler.FromCurrentSynchronizationContext), because we do have overloads of ContinueWith that take just TaskScheduler.


So why didn’t we add overloads of StartNew that take just TaskScheduler?  The short answer is that we were trying to reduce the number of overloads and didn’t expect this particular issue to be problematic.  It’s something we’d love to address, but unfortunately, adding the new overloads now could potentially break existing code that actually wanted to pass a TaskScheduler as the AsyncState.


Discussion is closed.

Feedback usabilla icon