C# 同步远程文件下载

C# 同步远程文件下载,c#,.net,multithreading,thread-safety,C#,.net,Multithreading,Thread Safety,序言:学习(并记住我已经知道的)C#线程、同步和数据结构是一项自我分配的纯语法任务 故事: 假设我有一个字典,它表示通过某个键指向文件的路径(http),即: foo => http://domain.tld/file1 bar => http://domain2.tld/file2 我想实现一个类,它将实现一个带有2个方法的接口: String Rand(); String Get(String key); 第一种方法是从所有可用文件中随机选取文件,而Get将返回一个特定的文件

序言:学习(并记住我已经知道的)C#线程、同步和数据结构是一项自我分配的纯语法任务

故事:

假设我有一个字典
,它表示通过某个键指向文件的路径(http),即:

foo => http://domain.tld/file1
bar => http://domain2.tld/file2
我想实现一个类,它将实现一个带有2个方法的接口:

String Rand();
String Get(String key);
第一种方法是从所有可用文件中随机选取文件,而
Get
将返回一个特定的文件,或者准确地说,返回下载文件的本地路径

该类应该是线程安全的,因此如果多个线程使用
Get()
请求相同的
key
,或者
Rand()
拾取相同的项,那么只有一个线程实际将文件下载到本地驱动器,或者如果文件已经下载,则应立即检索路径

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

我如何同步一个“下载程序”,使同一个文件不会被下载两次

如何限制同时下载的数量

PS:我不是问任何代码,只是问一些对这个任务有用的数据结构、类和模式的关键字

PPS:任务是100%抽象的,因此,如果您认为对需求的某些更改可以使其对我(作为学习者)更有趣/更有用,欢迎您进行更改。

因此,满足需求并使用
wait
/
async
的“downloader”类的“最终”版本是:

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";

    }
}
类下载程序
{
私人词典地图;
私有IDictionary_storage=新的ConcurrentDictionary();
私有ConcurrentDictionary_progress=新建ConcurrentDictionary();
公共下载程序(IDictionary map)
{
_map=map??新字典();
}
公共异步任务获取(字符串键)
{
字符串路径;
if(!\u映射TryGetValue(键,输出路径))
{
抛出新ArgumentException(“未找到指定的键”);
}
如果(_storage.ContainsKey(键))
{
返回存储[键];
}
任务;
if(_progress.TryGetValue(输入,输出任务))
{
返回等待任务;
}
任务=_检索文件(路径);
如果(!\u progress.TryAdd(键、任务))
{
返回等待获取(键);
}
_存储[键]=等待任务;
返回存储[键];
}
专用异步任务\u检索文件(字符串路径)
{
WriteLine(“已开始检索{0}”,路径);
等待任务。延迟(3000);
WriteLine(“已完成检索{0}”,路径);
返回路径+“本地路径”;
}
}

完整的代码和示例输出:

@I4V:MSDN链接实际上回答了我需要的所有问题。谢谢,除了通过线程同步之外key@I4V:但是我如何检查文件是否已经在本地显示或正在以线程安全的方式下载?如果您正在学习,为什么不学习正确的方法呢?没有进程像等待中的阻塞线程那样。使用。是的,你是对的。但是
\u存储
是。该死,这东西过去很难。使用async await只是一个自然的流程:)但是您跳过了真正的肉和土豆,等