4

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

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>>变成 a来完成我所需要的Task<T>,但它返回的包装器似乎实际上并不支持Start()- 它引发了一个异常。

TaskCompletionSource(我为稍微特殊的任务需求而去)似乎也没有任何设施来处理这类事情。

TaskExtensions.Unwrap()有没有支持“冷任务”的替代方案?

4

1 回答 1

6

您需要做的就是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();
    });
于 2013-08-13T09:19:26.847 回答