我正在寻找一种优雅的方式来缓存异步操作的结果。
我首先有一个这样的同步方法:
public String GetStuff(String url)
{
WebRequest request = WebRequest.Create(url);
using (var response = request.GetResponse())
using (var sr = new StreamReader(response.GetResponseStream()))
return sr.ReadToEnd();
}
然后我让它异步:
public async Task<String> GetStuffAsync(String url)
{
WebRequest request = WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var sr = new StreamReader(response.GetResponseStream()))
return await sr.ReadToEndAsync();
}
然后我决定我应该缓存结果,所以我不需要经常在外面查询:
ConcurrentDictionary<String, String> _cache = new ConcurrentDictionary<String, String>();
public async Task<String> GetStuffAsync(String url)
{
return _cache.GetOrAdd(url, await GetStuffInternalAsync(url));
}
private async Task<String> GetStuffInternalAsync(String url)
{
WebRequest request = WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var sr = new StreamReader(response.GetResponseStream()))
return await sr.ReadToEndAsync();
}
然后我读了一篇关于缓存如何Task<T>
更好的文章(或者观看了视频),因为创建它们很昂贵:
ConcurrentDictionary<String, Task<String>> _cache = new ConcurrentDictionary<String, Task<String>>();
public Task<String> GetStuffAsync(String url)
{
return _cache.GetOrAdd(url, GetStuffInternalAsync(url));
}
private async Task<String> GetStuffInternalAsync(String url)
{
WebRequest request = WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var sr = new StreamReader(response.GetResponseStream()))
return await sr.ReadToEndAsync();
}
现在的问题是,如果请求失败(例如:HTTP 401),缓存将包含失败Task<String>
,我将不得不重置应用程序,因为无法重新发送请求。
有没有一种优雅的方式ConcurrentDictionary<T1,T2>
来只缓存成功的任务并且仍然具有原子行为?