This is another post about a problem that I could have solved much quicker if I’d found the right blog posts. It’s common enough that .net 4.5 will include a new Task.Run
method that saves you from this mistake.
The symptom was that a task that was supposed to run in the background while the UI thread did some other work wasn’t starting – it was scheduled but didn’t run.
The reason was that the supposed background task was started from some code that was (further back in the call stack) inside a task that had been scheduled on the UI thread, using a common pattern of specifying the scheduler.
If you’re already inside a task, creating or starting a new task without specifying a scheduler picks up the scheduler of the parent task, not the default scheduler.
public void ButtonClicked() { var onUiThread = TaskScheduler.FromCurrentSynchronizationContext(); var task = new Task(() => DoSomethingInTheBackground()); task.ContinueWith(t => DoSomethingUIRelated(), onUiThread); task.Start(); } public void DoSomethingUIRelated() { var offUiTask = Task.Factory.StartNew(() => SomethingElseForTheBackground()); /// /// Do some UI-thread stuff /// offUiTask.Wait(); }
So, on the UI thread, a task is created which calls DoSomethingInBackground
. When that is finished, DoSomethingUIRelated
is called – on the UI thread by specifying the scheduler onUiThread
.
But when the task to call SomethingElseForTheBackground
is created, it inherits the UI thread scheduler, and because the UI thread starts a Wait
for it, it never gets a chance to run.
The solution is to specify TaskScheduler.Default
for offUiTask
, or to use the new Task.Run
method when .net 4.5 comes along.