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!
The issue is that the StartNew call will bind to the
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
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.