我对异步和Lambda函数有点困惑。
我有一个旧的CacheHelper函数(基本上是一个缓存预留模式),它可以执行以下操作:
public static T GetOrAdd<T>(Func<Task<T>> builder, TimeSpan expiresIn, bool ignoreNullValues, params string[] keys)
{
////check if the cache item is available
xxxx
//// if not, call the builder function to get fresh
var item = builder().Result;
//// add to cache and return the item
return item;
}
现在我们正在转向异步模式,所以这就是我调用缓存助手的方式:
CacheAsideHelper.GetOrAdd(
async () => await _currencyRepository.GetCurrencyInfo(currencyCode, commandTimeout, taskTimeout, _trackingId),
new TimeSpan(Constants.ExpirationDays, 0, 0), true, key);
我运行了一些测试,似乎可以预期结果。但是我的一位同事说,自从我传递异步lamda以来,我的缓存有时可能包含Task<T>
,而不是对象T。所以我需要以某种方式等待它。
但是,我的测试似乎也可以在我的Cache helper代码中提供正确的数据
var item = builder().Result;
缓存将始终包含真实数据(Task<T>
除外),我是对的还是我的同事是正确的?
答案 0 :(得分:0)
实际上可以缓存Task本身而不是结果,请参见https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/
作为一个类的任务非常灵活,并因此而受益。对于 例如,您可以由任意数量的消费者多次等待 同时。您可以将一个字典存储为任意数量的字典 随后的消费者在将来等待,这使得它成为 用作异步结果的缓存。
拥有一项任务(完成或未完成),您可以根据需要等待多次。因此,您可以执行以下操作:
public static Task<T> GetOrAdd<T>(Func<Task<T>> builder, string key, TimeSpan expiresIn)
{
//1. try find task in cache
//2. if task is failed or cancelled - run builder and put task into cache
//3. otherwise just return the task
}
仍然存在两个问题:
builder()
中的异常处理builder()
几次在这里您可以找到解决这两个问题的可能方法:https://github.com/MaximTkachenko/cache-once/blob/master/src/Mtk.CacheOnce/MemoryCacheOnceExtensions.cs