我有一个使用冷(未启动)任务的缓存类,以避免多次运行昂贵的东西。
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()
支持“冷任务”?
答案 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();
});