缓存异步函数导致C#

时间:2017-02-18 13:02:32

标签: c# caching asynchronous async-await task

我正在阅读“Cuts in a Nutshell”中的异步函数部分。一个例子如下:

假设我们需要处理“下载”按钮点击事件,我们想要缓存下载结果。如果我们不关心下载相同的网站,以防重复点击该按钮,那么它就是eaiser。

private static Dictionary<string, string> _cache = new Dictionary<string, string>();
    async Task<string> DownloadAsync(string uri)
    {
        string content;
        if (_cache.TryGetValue(uri, out content)) return content;
        return _cache[uri] = await new WebClient().DownloadStringTaskAsync(uri);
    }

但是这意味着它会向同一个网站发出多余的下载,例如,如果连续两次点击它!为了保护这一点,本书建议缓存Task<string>

private static Dictionary<string, Task<string>> _futureCache = new Dictionary<string, Task<string>>();
Task<string> GetWebPage(string uri)
    {
        lock (_futureCache)
        {
            Task<string> task;
            if (_futureCache.TryGetValue(uri, out task)) return task; 
            return _futureCache[uri] = new WebClient().DownloadStringTaskAsync(uri);
        }
    }

但我不明白这种保护是如何有效的。我假设click事件处理程序是这样的:

 _downloadBtn.Click += async (sender, args) =>
        {
            var uri = "http://www.bbc.co.uk"; // for argument sake, it always downloads bbc...
            string result = await GetWebPage(uri);
            // process the result...
        };

如果由于某种原因,第一次点击触发的下载仍在进行中,然后第二次点击该按钮;是不是仍会继续下载页面两次,因为在第二次点击被触发时尚未从第一次下载填充缓存?如果我的理解是错误的,请解释原因。否则,如何实现这样的缓存保护重复用户点击?

顺便说一下,我知道如果缓存在事件处理程序(UI)上下文之外使用,缓存有效地工作,例如下面只下载一次。

    async void Foo()
    {
        var uri = "http://www.bbc.co.uk";
        string result;
        for (int i = 0; i < 2; i++) //Stimulate repeated call
        {
            result = await GetWebPage(uri); 
            Console.WriteLine(result.Length);
        }
    }

1 个答案:

答案 0 :(得分:1)

_futureCache字典基于Task密钥缓存uri。它会阻止您在请求网页时创建新的Task,而是会返回上次Task请求时已创建的uri - 无论是否Task已完成。然后,对于Task的每个请求,调用代码等待相同的uri。如果已经完成,则立即返回Task的结果。如果没有,它会一直等到它完成。无论如何,它只会获取一次页面。