Cold Tasks和TaskExtensions.Unwrap

时间:2013-08-13 05:21:25

标签: c# task-parallel-library .net-4.5 async-await c#-5.0

我有一个使用冷(未启动)任务的缓存类,以避免多次运行昂贵的东西。

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;
    }
}

这很有效,直到您的任务实际使用C#5的await实现,ala

cache.GetOrAddAsync("key", new Task(async () => {
  var r = await AsyncOperation();
  return r.FastSynchronousTransform();
}));)`

现在看来TaskExtensions.Unwrap()完全符合我的要求,将Task<Task<T>>转换为Task<T>,但似乎它返回的包装器实际上并不支持Start() - 它引发了一个例外。

TaskCompletionSource(对于稍微特殊的任务需求而言)似乎没有任何设施可用于此类事情。

是否有替代TaskExtensions.Unwrap()支持“冷任务”?

1 个答案:

答案 0 :(得分:5)

您需要做的就是在打开它之前保留Task并开始:

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;
}

用法:

cache.GetOrAddAsync(
    "key", async () =>
    {
        var r = await AsyncOperation();
        return r.FastSynchronousTransform();
    });