Cold Tasks and TaskExtensions.Unwrap
.net-4.5, async-await, c#, c#-5.0, task-parallel-library
Solution
All you need to do is to keep the `Task` before unwrapping it around and start that:
public Task<TValue> GetOrAddAsync(TKey key, Func<Task<TValue>> taskFunc)
{
Task<Task<TValue>> wrappedTask = new Task<Task<TValue>>(taskFunc);
Task<TValue> unwrappedTask = wrappedTask.Unwrap();
Task<TValue> cachedTask = base.GetOrAdd(key, unwrappedTask);
if (cachedTask == unwrappedTask)
wrappedTask.Start();
return cachedTask;
}
Usage:
cache.GetOrAddAsync(
"key", async () =>
{
var r = await AsyncOperation();
return r.FastSynchronousTransform();
});
Problem
I've got a caching class that uses cold (unstarted) tasks to avoid running the expensive thing multiple times. ``` public class AsyncConcurrentDictionary<TKey, TValue> : System.Collections.Concurrent.ConcurrentDictionary<TKey, Task<TValue>> { internal Task<TValue> GetOrAddAsync(TKey key, Task<TValue> newTask) { var cachedTask = base.GetOrAdd(key, newTask); if (cachedTask == newTask && cachedTask.Status == TaskStatus.Created) // We won! our task is now the cached task, so run it cachedTask.Start(); return cachedTask; } } ``` This works great right up until your task is actually implemented using C#5's `await`, ala ``` cache.GetOrAddAsync("key", new Task(async () => { var r = await AsyncOperation(); return r.FastSynchronousTransform(); }));)` ``` Now it looks like `TaskExtensions.Unwrap()` does exactly what I need by turning `Task<Task<T>>` into a `Task<T>`, but it seems that wrapper it returns doesn't actually support `Start()` - it throws an exception. `TaskCompletionSource` (my go to for slightly special Task needs) doesn't seem to have any facilities for this sort of thing either. Is there an alternative to `TaskExtensions.Unwrap()` that supports "cold tasks"?