There is a class TaskCompletionSource<TResult>
as described in The Nature of TaskCompletionSource<TResult>
:
The TaskCompletionSource<TResult>
type serves two related purposes, both alluded to by its name: it is a source for creating a task, and the source for that task’s completion. In essence, a TaskCompletionSource<TResult>
acts as the producer for a Task<TResult>
and its completion. You create a TaskCompletionSource<TResult>
and hand the underlying Task<TResult>
it’s created, accessible from its Task property. Unlike Tasks created by Task.Factory.StartNew, the Task handed out by TaskCompletionSource<TResult>
does not have any scheduled delegate associated with it
To me, a classic scenario for using TaskCompletionSource is when it’s possible that my method won’t necessarily have to do a time consuming operation. What it allows us to do is to choose the specific cases where we’d like to use a new thread.
A good example for this is when you use a cache. You can have a GetResourceAsync method, which looks in the cache for the requested resource and returns at once (without using a new thread, by using TaskCompletionSource) if the resource was found. Only if the resource wasn’t found, we’d like to use a new thread and retrieve it using Task.Run().
using System.Threading;
using System.Threading.Tasks;
public class AsyncManualResetEvent
{
private TaskCompletionSource taskCompletionSource = new TaskCompletionSource();
public Task WaitAsync()
{
return taskCompletionSource.Task;
}
public void Set()
{
TaskCompletionSource tcs = taskCompletionSource;
Task.Factory.StartNew(s => ((TaskCompletionSource) s).TrySetResult(true),
tcs,
CancellationToken.None,
TaskCreationOptions.PreferFairness,
TaskScheduler.Default);
tcs.Task.Wait();
}
public void Reset()
{
while (true)
{
TaskCompletionSource tcs = taskCompletionSource;
if (!tcs.Task.IsCompleted ||
Interlocked.CompareExchange(ref taskCompletionSource, new TaskCompletionSource(), tcs) == tcs)
{
return;
}
}
}
}
public static class TaskExtensions
{
public static Task AsAwaitable(this CancellationToken token)
{
var ev = new AsyncManualResetEvent();
token.Register(() => ev.Set());
return ev.WaitAsync();
}
}
Source
- How to await a CancellationToken by SocialEbola
- Building Async Coordination Primitives, Part 1: AsyncManualResetEvent by Stephen Toub
- Real life scenarios for using TaskCompletionSource from StackOverflow
- Diving deep with WinRT and await by Stephen Toub from Parallel Extensions Team