C# DownloadFileTaskAsync无法下载数千个文件

C# DownloadFileTaskAsync无法下载数千个文件,c#,webclient,C#,Webclient,我有上千个下载url列表。url可能包含不同类型的文件,如图像、pdf、音频、视频等。我正在尝试使用DownloadFileTaskAsync下载它们。但在下载了几个文件后,该应用程序出现了故障。我不明白发生了什么事。不要收到任何错误消息。只是我的应用程序在下载了几个文件后被关闭了 foreach (var url in urls) { //if file exists in our local directory then do not need to download and con

我有上千个下载url列表。url可能包含不同类型的文件,如图像、pdf、音频、视频等。我正在尝试使用DownloadFileTaskAsync下载它们。但在下载了几个文件后,该应用程序出现了故障。我不明白发生了什么事。不要收到任何错误消息。只是我的应用程序在下载了几个文件后被关闭了

foreach (var url in urls)
{
    //if file exists in our local directory then do not need to download and continue the process...
    if (FileExistsOrNot(localPath + "/" + url.fileName))
       continue;

    Thread thread = new Thread(async () => {
        WebClient client = new WebClient();
        client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
        client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
        await client.DownloadFileTaskAsync(new Uri(url.downloadUrl), localPath + "/" + url.fileName);  
    });
    thread.Start();
}

每次下载都会产生一个新线程,难怪在这种情况下,系统资源线程会很快用完

您应该使用某种线程池,幸运的是,C有多个现成的解决方案来实现这一点:

线程池- 并行程序设计- 任务+任务计划程序- ThreadPool是最低级别的构造,并行编程和任务调度程序都是建立在它之上的

当您使用异步操作下载DownloadFileTaskAsync时,任务是这种情况下最合适的选项:

Action<string, DownloadProgressChangedEventArgs> onDownloadProgress = (url, e) => 
{ 
    /* your logic displaying progress... */ 
};

var downloadTasks = urls
    .Where(url => !FileExistsOrNot(Path.Combine(localPath, url.fileName)))
    .Select(async url =>
    {
        using (var client = new WebClient())
        {
            client.DownloadProgressChanged += (s, e) => onDownloadProgress(url.fileName, e);
            try { await client.DownloadFileTaskAsync(new Uri(url.downloadUrl), Path.Combine(localPath, url.fileName)); }
            catch (Exception ex) { /* handle download error: log exception, etc */ }                
        }
    });    

Task.WaitAll(downloadTasks.ToArray()); // or Task.WhenAll(...) if you want it non-blocking
一些评论:

使用Path.Combine构建路径字符串,以在每个平台上获得正确的结果 WebClient实现IDisposable,也就是说,它可能使用非托管资源。当您不再需要Dispose时,应该立即调用它,否则您可能会再次耗尽系统资源。 您不需要观察DownloadFileCompleted。等待下载FileTaskAsync将潜在错误作为异常重新提交。 考虑使用WebClient代替更好的异步支持和性能
DownloadFileTaskAsync已经承诺在后台和服务器上完成这项工作。为什么创建新线程只是为了设置下载?另外,您正在为DownloadFileTaskAsync未触发的事件订阅两个事件处理程序。感谢您的评论。但我看到了一件事。当我的文件名包含任何空间时,问题就发生了,并使应用程序崩溃。创建数千个线程不是一个好主意-早晚你都会得到OutOfMemoryException。另外,操作系统也不能有效地调度这么多线程。由于数据包在堵塞的internet连接中丢失,与服务器的连接可能会断开。例如,关于这一点的问题很多。您还需要正确处理下载线程上引发的异常,因为线程上任何未处理的异常都会立即终止您的应用程序。首先,您没有异常处理程序来指示应用程序中是否发生了错误。第二大Web服务器,因此不允许来自同一IP的多个连接来防止服务攻击。因此,服务器可能会关闭您,因为它认为您的应用程序是一种攻击。