"C#in a Nutshell"の非同期関数セクションを読んでいます。 1つの例は以下の通りです:キャッシュ非同期関数の結果が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);
}
しかし、たとえば、2回連続してクリックすると、同じウェブサイトに冗長なダウンロードが行われます。これを守るために、本書では代わりに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);
}
}
しかし、この保護がどのように効果的なのか理解できません。私は、クリックイベントハンドラはこのようなものになるだろうと仮定します。何らかの理由で、最初のクリックによってトリガダウンロードはプロセスにまだあるし、ボタンが2回目のためにクリックされ、場合
_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回もダウンロードする予定はありませんか?私の理解が間違っているなら、理由を説明してください。さもなければ、そのようなキャッシュprotecingを実装する方法は繰り返しユーザーのクリックですか?
ちなみに、キャッシュはイベントハンドラ(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'には' await'がありません:) –
@LucasTrzesniewskiそれは意図的に並行処理を待つ必要はありません! – stt106
正確に、コードはタスクがすでに進行中でありますときに、第2のタスクを追加しません(あるいはそれがすでにそのことについて完了の場合):) –