Consider the following code:
public Task SomeAsyncMethod()
{
var tcs = new TaskCompletionSource();
... do something, NOT setting the TaskCompletionSource...
return tcs.Task
}
public void Callsite1()
{
await SomeAsyncMethod();
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
public void Callsite2()
{
SomeAsyncMethod().ContinueWith((task) =>
{
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
});
}
At some point in time, the TaskCompletionSource created in SomeAsyncMethod is set on a ThreadPool Thread:
Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
tcs.SetResult();
When the Task from the TaskCompletionSource is awaited (as in Callsite1), the continuation executes synchronously on the Thread that called SetResult. When ContinueWith is called (as in Callsite2), the continuation executes asynchronously on a different thread.
It does not help to call configure the await, as in
public void Callsite1()
{
await SomeAsyncMethod().ConfigureAwait(true or false);
}
and this is not even the point of this question. As the implementor of SomeAsyncMethod, I do not want to call some potentially unknown code by calling SetResult. I want to have the continuation always scheduled asynchronously. And I cannot rely on the caller to configure the await properly (if that would even work).
How can a TaskCompletionSource be configured such that its Task doesn’t execute its continuation synchronously when it’s awaited?
There is no way to prevent synchronous task continuations. Normally, this is not a problem.
However, there are some situations where you do need this, e.g., if you are completing the task while holding a lock. In those cases, you can just
Task.Runthe task completion, as such:This is an advanced technique. It is an exception to the guideline “Don’t block on
asynccode (asyncall the way down)” as expounded on my blog.It’s part of my AsyncEx library, as an extension method:
This technique was first published by Stephen Toub on his blog.