2017-02-18 9 views
0

"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); 
     } 
    } 
+1

そのコードは正常に動作します。ヒント: 'GetWebPage'には' await'がありません:) –

+0

@LucasTrzesniewskiそれは意図的に並行処理を待つ必要はありません! – stt106

+0

正確に、コードはタスクがすでに進行中でありますときに、第2のタスクを追加しません(あるいはそれがすでにそのことについて完了の場合):) –

答えて

1

_futureCache辞書はuriキーに基づいてTaskをキャッシュされます。 Webページを尋ねるときに新しいTaskを作成することができなくなり、その前に既に作成されたTaskが返されます。uriが要求されたかどうか - Taskが完了しているかどうか。その後、呼び出しコードは、そのすべての要求に対して同じTaskを待つことになります。uriすでに完了している場合は、すぐに結果がTaskになります。そうでなければ、終了するまで待つ。何があっても、一度ページを取得するだけです。

+0

たぶん私はあなたを理解していないよしかし、 URIへの第一の呼び出しは、と何のキャッシュがない場合は、このダウンロードが進行している間、あなたはキャッシュが現在進行中のタスクにすでに移入されていると言って、ダウンロードを要求でしょうか? – stt106

+0

'GetWebPage'は' async'とマークされていません。タスクを返しますが、完了するのを待つことはありません。したがって、競合状態を防ぐために '_futureCache'の監視ロックが保持されている間はキャッシュに入ります。 2番目の要求は、タスクが*開始されて*キャッシュに追加されるまで数ナノ秒間ブロックされます。それがすでにキャッシュされていますまだ進行中だにも関わらず、例えば、すぐに最初のタスクが開始されると私の最初のコメントと同じことを意味し – Tim

+0

[OK]を考えてみてください。だから、2番目の要求がキャッシュから不完全なタスクを取得する可能性がありますが、まだ別の要求/タスクをトリガしないのだろうか? – stt106

関連する問題