Assume I created a library containing such method:
Task MyLibraryMethodAsync()
{
var taskCompletionSource = new TaskCompletionSource<object>();
Action myWorkItem =
() =>
{
// Simulate some work.
// Actual work items depend on input params.
Thread.Sleep(TimeSpan.FromSeconds(1));
taskCompletionSource.SetResult(null);
};
// The next two lines is simplification for demonstration.
// I do not have access to the workerThread - it is created
// and managed for me by another lib.
// All I can do - is to post some short work items to it.
var workerThread = new Thread(new ThreadStart(myWorkItem));
workerThread.Start();
return taskCompletionSource.Task;
}
Any user of my lib can call MyLibraryMethodAsync
like this
await MyLibraryMethodAsync().ConfigureAwait(false);
VeryLongRunningMethod();
void VeryLongRunningMethod()
{
Thread.Sleep(TimeSpan.FromHours(1));
}
And here the problem comes – VeryLongRunningMethod
will be executed inside the taskCompletionSource.SetResult(null)
call and thus it will block workerThread
for a long period of time, which is not desired behavior because workerThread
is intended to run small portions of code (work items).
How do I substitute context / scheduler to a thread pool inside the returned task making await x.ConfigureAwait(false)
to continue on the thread pool, but not on the workerThread
?
The current solution I found is
Task MyLibraryMethodAsync()
{
// ...
return taskCompletionSource.Task
.ContinueWith(x => x.Result, TaskScheduler.Default);
}
However, I do not like it due to overhead it creates. May be more elegant solution exists?
As of .NET 4.6 there is an option in TaskCreationOptions
called RunContinuationsAsynchronously
, which does exactly what you want, it ensures that all continuations are run asynchronously, rather than run synchronously when setting the result. TaskCompletionSource
has an optional TaskCreationOption
parameter in its constructor for you to provide that option.
If you're using an earlier version of .NET you'll need to do a less efficient hack, such as adding another continuation, as you showed, or explicitly setting the result in a thread pool thread, rather than through the callback action.