Я читаю раздел асинхронной функции в «C# в двух словах». Один из примеров следующий:Результат асинхронной кеширования в C#
Предположим, нам нужно обработать событие щелчка кнопки «Загрузить», и мы хотим кэшировать результаты загрузки. Если мы не заботимся о том, чтобы загрузить сайт в случае повторного нажатия кнопки, то это 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);
}
}
Но я не понимаю, как эта защита может быть эффективной. Я предполагаю, что обработчик события клика будет что-то вроде этого:
_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...
};
Если по какой-то причине загрузка запускается первым кликом еще в процессе, а затем кнопка нажата в течение 2-я раза; не собирается ли он еще дважды загрузить страницу, так как кэш еще не был заполнен с первой загрузки к моменту запуска 2-го клика? Если мое понимание ошибочно, объясните, почему. В противном случае, как реализовать такой кеш-защиту, повторный щелчок пользователя?
Кстати, я понимаю, что кеш работает эффективно, если он используется вне контекста обработчика событий (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);
}
}
Этот код работает нормально. Подсказка: в 'GetWebPage' нет' ожидания ':) –
@LucasTrzesniewski это намеренно не ждет, чтобы спровоцировать параллелизм! – stt106
Точно, код не добавит второй задачи, когда уже выполняется задание (или если оно уже завершено в этом отношении) :) –