The Problem
-----------
Solution
--------
There is a class [TaskCompletionSource](https://msdn.microsoft.com/en-us/library/dd449174.aspx) as described in [The Nature of TaskCompletionSource](http://blogs.msdn.com/b/pfxteam/archive/2009/06/02/9685804.aspx):
The TaskCompletionSource 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 acts as the producer for a Task and its completion. You create a TaskCompletionSource and hand the underlying Task it’s created, accessible from its Task property. Unlike Tasks created by Task.Factory.StartNew, the Task handed out by TaskCompletionSource does not have any scheduled delegate associated with it
[CancellationToken](https://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.aspx)
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](https://socialeboladev.wordpress.com/2013/03/05/how-to-await-a-cancellationtoken/) by SocialEbola
* [Building Async Coordination Primitives, Part 1: AsyncManualResetEvent](http://blogs.msdn.com/b/pfxteam/archive/2012/02/11/10266920.aspx) by Stephen Toub
* [Real life scenarios for using TaskCompletionSource](http://stackoverflow.com/questions/15316613/real-life-scenarios-for-using-taskcompletionsourcet) from StackOverflow
* [Diving deep with WinRT and await](http://blogs.msdn.com/b/windowsappdev/archive/2012/04/24/diving-deep-with-winrt-and-await.aspx) by Stephen Toub from [Parallel Extensions Team](http://blogs.msdn.com/b/pfxteam/)