2013-05-09 71 views
0

序言:這是一個自我分配的純同步任務來學習(並記住我已經知道的)C#線程以及同步和數據結構。遠程文件同步下載

故事:

比方說,我有一本字典<string, string>通過一些鍵表示一個路徑(HTTP)到一個文件,即:

foo => http://domain.tld/file1 
bar => http://domain2.tld/file2 

而且我想實現一個類這將實現一個接口與2種方法:

String Rand(); 
String Get(String key); 

第一種方法將來自所有可用挑選文件隨機和Get將ř修改一個特定的文件,或者確切地說 - 下載文件的本地路徑。

類應該是線程安全的,因此,如果多個線程請求相同keyGet()Rand()採同一項目 - 然後只有一個線程應在文件實際下載到本地驅動器或路徑如果文件已被下載,應立即檢索。

所以,這就是我陷入困境的地方。

我該如何同步「下載程序」,以便相同的文件不會被下載兩次?

我怎樣才能限制同時下載量? PS:我不問任何代碼,只是一個關鍵字到數據結構,類和模式,這將是有用的這項任務。

PPS:任務是100%抽象的,所以如果你認爲對需求的一些改變可以使它對我更有趣/有用(作爲一個學習者) - 歡迎你的改變。

+0

@ I4V:其實MSDN鏈接回答所有我需要的感謝,除了同步線程 – zerkms 2013-05-09 10:05:51

+0

@ I4V:但我如何檢查一個文件是否已經在本地呈現或以線程安全方式下載? – zerkms 2013-05-09 10:27:53

+1

如果你在學習,爲什麼不學習*正確*的方式來做到這一點?沒有進程喜歡在等待中阻塞線程。使用[await async](http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx)。 – 2013-05-09 12:44:18

回答

0

所以滿足要求,和使用await/async一個 「下載」 類的 「最終」 版本是:

class Downloader 
{ 
    private IDictionary<string, string> _map; 
    private IDictionary<string, string> _storage = new ConcurrentDictionary<string, string>(); 
    private ConcurrentDictionary<string, Task<string>> _progress = new ConcurrentDictionary<string,Task<string>>(); 

    public Downloader(IDictionary<string, string> map) 
    { 
     _map = map ?? new Dictionary<string, string>(); 
    } 

    public async Task<string> Get(string key) 
    { 
     string path; 

     if (!_map.TryGetValue(key, out path)) 
     { 
      throw new ArgumentException("The specified key wasn't found"); 
     } 

     if (_storage.ContainsKey(key)) 
     { 
      return _storage[key]; 
     } 

     Task<string> task; 
     if (_progress.TryGetValue(key, out task)) 
     { 
      return await task; 
     } 

     task = _retrieveFile(path); 

     if (!_progress.TryAdd(key, task)) 
     { 
      return await Get(key); 
     } 

     _storage[key] = await task; 
     return _storage[key]; 
    } 

    private async Task<string> _retrieveFile(string path) 
    { 
     Console.WriteLine("Started retrieving {0}", path); 
     await Task.Delay(3000); 
     Console.WriteLine("Finished retrieving {0}", path); 
     return path + " local path"; 

    } 
} 

整個代碼示例輸出:http://pastebin.com/LdFvPDbQ