2017-02-18 63 views
0

我讀的「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); 
     } 
    } 

但我不明白,這種保護是如何有效。我認爲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

該代碼正常工作。提示:'GetWebPage'中沒有'await' :) –

+0

@LucasTrzesniewski它故意沒有等待煽動併發! – stt106

+0

沒錯,當已經有進行中的任務(或者,如果它已經完成了對這個問題)的代碼不會添加第二個任務:) –

回答

1

_futureCache字典是基於uri關鍵緩存一個Task。要求該網頁時,它阻止您創建一個新的Task,而是將返回一個已經創建的最後一次uri是request--是Task是否已完成Task。然後調用代碼等待相同的Task對該uri的每個請求。如果已經完成,則立即返回該結果Task。如果沒有,它會一直等到它完成。無論如何,它只會獲取一次頁面。

+0

也許我不理解你,但當第一次調用uri並且沒有緩存時,它會請求下載,而此下載正在進行中,您是否說緩存已經填充到正在進行的任務中? – stt106

+0

'GetWebPage'沒有標記'async'。它會返回一個任務,但不會等待它完成。所以它保存在緩存中,同時監視'_futureCache'上的監視器鎖定以防止競爭條件。第二個請求會在那裏阻塞幾個納秒,直到Task開始*並添加到緩存。 – Tim

+0

好的,我認爲這意味着和我的第一個評論一樣,比如一旦第一個任務開始,即使它仍然在進行,它已經被緩存了。所以第二個請求可能會從緩存中得到一個不完整的任務,但仍然不會觸發另一個請求/任務? – stt106