使用 IMemoryCache 缓存结果 Task<T> 的正确方法
Posted
技术标签:
【中文标题】使用 IMemoryCache 缓存结果 Task<T> 的正确方法【英文标题】:Proper way to cache results Task<T> with IMemoryCache 【发布时间】:2018-07-22 23:02:57 【问题描述】:我正在寻找一种方法来包装一个为某些数据调用远程 WCF 服务的服务。例如,此类服务可能如下所示
public interface IAsyncSvc
Task<int[]> GetData(int key);
public class SomeAsyncSvc:IAsyncSvc
public Task<int[]> GetData(int key)
Console.WriteLine("SomeAsyncSvc::GetData()");
return Task.Factory.StartNew(() =>
//Some time-consuming operation
Thread.Sleep(1000);
return Enumerable.Range(1, key).ToArray();
);
我第一次写了一个简单的缓存包装器:
public class SomeAsyncSvcCachedTask : IAsyncSvcCached
private readonly IAsyncSvc _svc;
private readonly IMemoryCache _cache;
public SomeAsyncSvcCachedTask(IAsyncSvc svc, IMemoryCache cache)
_svc = svc;
_cache = cache;
public Task<int[]> GetData(int v)
if (_cache.TryGetValue(v, out Task<int[]> cacheEntry))
return cacheEntry;
var task = _svc.GetData(v);
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(5));
_cache.Set(v, task, cacheEntryOptions);
return task;
缓存Task的主要缺点是,如果第一次尝试缓存失败,我会缓存一个出错的任务,并一遍又一遍地查询Task.Result,直到另一个未缓存的成功调用。
然后我为它写了另一个包装器:
public class SomeAsyncSvcCached : IAsyncSvcCached
private readonly IAsyncSvc _svc;
private readonly IMemoryCache _cache;
public SomeAsyncSvcCached(IAsyncSvc svc, IMemoryCache cache)
_svc = svc;
_cache = cache;
public Task<int[]> GetData(int v)
if (_cache.TryGetValue(v, out int[] cacheEntry))
return Task.FromResult(cacheEntry);
var task = _svc.GetData(v);
task.ContinueWith(t =>
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(5));
_cache.Set(v, t.Result, cacheEntryOptions);
, TaskContinuationOptions.NotOnFaulted);
return task;
主要思想不是缓存Task<int[]>
,而只是缓存int[]
类型的结果。作为一个优势,如果第一次调用失败,那么这个包装器会尝试一遍又一遍地读取数据,而不是返回缓存的错误。
这种方法有什么缺陷?也许有一种更简单的方法来实现缓存返回Task<>
的方法调用的目标?
【问题讨论】:
【参考方案1】:看一下文章: http://cpratt.co/thread-safe-strongly-typed-memory-caching-c-sharp/
public static async Task<T> AddOrGetExistingAsync<T>(
this ObjectCache cache,
string key,
Func<Task<T>> valueFactory,
CacheItemPolicy policy)
var newValue = new AsyncLazy<T>(valueFactory);
var oldValue = cache.AddOrGetExisting(key, newValue, policy) as Lazy<T>;
try
return oldValue != null ? oldValue.Value : await newValue.Value;
catch
cache.Remove(key);
throw;
【讨论】:
以上是关于使用 IMemoryCache 缓存结果 Task<T> 的正确方法的主要内容,如果未能解决你的问题,请参考以下文章
在ASP.NET Core应用中使用IMemoryCache缓存
在ASP.NET Core应用中使用IMemoryCache缓存
在ASP.NET Core应用中使用IMemoryCache缓存
在ASP.NET Core应用中使用IMemoryCache缓存