缓存异步操作 [英] Caching asynchronous operations

查看:230
本文介绍了缓存异步操作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要寻找我的缓存异步操作结果的一种优雅的方式。

I am looking for an elegant way of caching the results of my asynchronous operations.

我第一次碰到这样的同步方法:

I first had a synchronous method like this:

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();
}

然后我做了异步的:

Then I made it asynchronous:

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();
}

于是我决定,我要缓存结果,所以我并不需要外界经常查询:

Then I decided that I should cache the results, so I do not need to query outside that often:

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();
}

然后我读了一篇文章(O观看视频)如何缓存任务&LT; T&GT; 比较好,因为他们创造是昂贵的:

Then I read an article (o watched a video) about how caching Task<T> is better, because creating them is expensive:

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),高速缓存将包含失败任务&LT;字符串&GT; ,我将不得不重置程序,因为这将是不可能的重新发送请求。

And now the problem is, that if the request fails (e.g.: a HTTP 401), the cache will contain a failed Task<String> and I will have to reset the app because it will be impossible to resend the request.

有没有使用的优雅的方式 ConcurrentDictionary&LT; T1,T2&GT; 缓存唯一成功的任务,仍然有原子行为

Is there a elegant way of using ConcurrentDictionary<T1,T2> to cache only successful tasks and still have the atomic behavior?

推荐答案

首先,无论你的方法是错误的,因为他们不救你的任何要求(虽然第二个至少可以节省您的时间)。

First of all, both your approaches are wrong, because they don't save you any requests (though the second one at least saves you time).

您最初的code(一个与等待)做到这一点:

Your first code (the one with await) does this:

  1. 请请求。
  2. 等待请求完成。
  3. 如果已经有了另外一个结果在缓存中,忽略请求的结果。

你的第二个code删除步骤2中,所以它的速度更快,但你仍然让很多不必要的请求。

Your second code removes step 2, so it's faster, but you're still making lots of unnecessary requests.

你应该做的是使用 的GetOrAdd()<过载/ code>,需要一个委托

public Task<String> GetStuffAsync(String url)
{
    return _cache.GetOrAdd(url, GetStuffInternalAsync);
}

这并不能完全消除被忽略请求的可能性,但它确实使它们不太可能。 (对于这一点,你可以尝试取消您所知道的被忽略的请求,但我不认为这是值得的在这里。)

This doesn't completely eliminate the possibility of requests that are ignored, but it does make them much less likely. (For that, you could try canceling requests that you know are being ignored, but I don't think that's worth the effort here.)

现在您的实际问题。我想你应该做的是使用 AddOrUpdate()。如果该值是现在还没有,添加它。如果它的存在,替换它,如果它出现故障的:

Now to your actual question. What I think you should do is to use the AddOrUpdate() method. If the value isn't there yet, add it. If it's there, replace it if it's faulted:

public Task<String> GetStuffAsync(String url)
{
    return _cache.AddOrUpdate(
        url, GetStuffInternalAsync, (u, task) =>
        {
            if (task.IsCanceled || task.IsFaulted)
                return GetStuffInternalAsync(u);
            return task;
        });
}

这篇关于缓存异步操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆