2013-05-09 4 views
0

Преамбула: это самостоятельная и чистая синтаксическая задача, чтобы узнать (и запомнить, что я уже знал) потоки C#, а также структуры синхронизации и данных.Синхронизация удаленных файлов download

История:

Скажем, у меня есть словарь <string, string>, который представляет собой путь (HTTP) в файл с помощью какой-то ключ, то есть:

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

И я хотел бы реализовать класс которые будут реализовывать интерфейс с 2-мя методами:

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

Первый метод будет выбрать файл случайным образом из всех доступных, и Get будет г eturn конкретный файл, а точнее - локальный путь к загруженному файлу.

Класс должен быть поточно-, так что если несколько потоков просить же key с Get() или Rand() выбирает один и тот же предмет - тогда только один поток должен фактически загрузить файл на локальный диск или путь должны быть получены немедленно, если файл уже загружен.

Итак, вот где я застрял.

Как бы синхронизировать «загрузчик», чтобы тот же файл не загружался дважды?

Как я могу ограничить количество одновременной загрузки?

PS: Я не спрашиваю о каком-либо коде, просто ключевые слова для структур данных, классов и шаблонов, которые были бы полезны для этой задачи.

PPS: задача 100% абстрактная, поэтому, если вы считаете, что некоторые изменения в требованиях могут сделать ее более интересной/полезной для меня (как учащегося) - вы можете с изменениями.

+0

@ I4V: MSDN ссылки фактически ответьте на все, что мне нужно для этого, за исключением потоков синхронизации по ключу – zerkms

+0

@ I4V: но как бы проверить, есть ли файл уже локально или загружается поточно-безопасным способом? – zerkms

+1

Если вы участвуете в обучении, почему бы не научиться правильно это делать? Ни один процесс не любит блокировать потоки в ожидании. Используйте [ожидание async] (http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx). –

ответ

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

Смежные вопросы