I tried to find an answer for this but couldn't. What I was wondering is that on which thread Task.ContinueWith
delegate is called.
For await I know that it tries to run it on the captured SynchronizationContext
but there is nothing documented for ContinueWith
.
I also tried with a sample program and though it seems it is called on Threadpool
thread, I suspect that in some scenario it might call on SynchronizationContext
. Maybe someone can provide a definitive answer.
This depends on the scheduler that is associated with the continuation. By default, task continuations are scheduled through the
Current
scheduler, being theTaskScheduler
associated with the currently executing task. WhenContinueWith
is not called from within a task,Current
will return theDefault
scheduler, which is the defaultTaskScheduler
instance provided by the .NET Framework, and which will schedule your tasks on the thread pool.If you want to influence this behaviour, you can call one of the
ContinueWith
overloads that takes aTaskScheduler
parameter. A common pattern is to passTaskScheduler.FromCurrentSynchronizationContext()
when creating continuations on the UI thread, as this would cause the continuation to be dispatched back onto the UI thread when executed.Edit: In reply to your comment: The deadlock may arise if you spawn a child task (intended to run on the thread pool) from a continuation running on the UI thread. In such cases, the child task will inherit the task scheduler from the parent task, which would be bound to the UI thread, causing the child task to run on the UI thread too.
To resolve this, you can use the
StartNew
overload that accepts aTaskScheduler
parameter for the child task, and passTaskScheduler.Default
to it:Task.ContinueWith
is scheduled on theTaskScheduler.Current
unless specified otherwise by the parameters in one of the optional overloads.If you don't have a custom scheduler in
TaskScheduler.Current
(which is very likely) your continuation will run on theThreadPool
.Task.ContinueWith
never uses theSynchronizationContext
unless you create aTaskScheduler
out of it withTaskScheduler.FromCurrentSynchronizationContext
.You can always state explicitly which
TaskScheduler
is needed using one of the available overloads: