C# 等待所有异步下载完成

C# 等待所有异步下载完成,c#,asynchronous,download,C#,Asynchronous,Download,我正在尝试运行一些下载多个文件的代码,并等待所有文件完成后再继续执行代码 我目前正在运行这部分代码,它实际上会按预期下载所有内容,但不会继续执行。。程序冻结了 如果你能帮我解决这个问题就太好了,谢谢 private void DownloadFiles(string[] targets, string sub_id, string base_url) { var tasks = new List<Task>(); int c = 0; foreach (va

我正在尝试运行一些下载多个文件的代码,并等待所有文件完成后再继续执行代码

我目前正在运行这部分代码,它实际上会按预期下载所有内容,但不会继续执行。。程序冻结了

如果你能帮我解决这个问题就太好了,谢谢

private void DownloadFiles(string[] targets, string sub_id, string base_url)
{
    var tasks = new List<Task>();
    int c = 0;

    foreach (var target in targets)
    {
        if (target.EndsWith(".vtt", StringComparison.InvariantCultureIgnoreCase))
        {
            using (var wc = new WebClient())
            {
                var task = DownloadFile(wc, base_url + sub_id + "/" + target, sub_id + "." + c++ + ".vtt");
                tasks.Add(task);
            }
        }
    }
    Task.WaitAll(tasks.ToArray());
}


private Task DownloadFile(WebClient wc, string target, string name)
{
    wc.DownloadProgressChanged += (object sender, DownloadProgressChangedEventArgs e) =>
    {
        Console.WriteLine(e.ProgressPercentage + "% downloaded.");
    };

    wc.DownloadFileCompleted += (object sender, AsyncCompletedEventArgs e) =>
    {
        Console.WriteLine(target + " was downloaded.");
    };

    return wc.DownloadFileTaskAsync(target, Environment.CurrentDirectory + "/Subs/" + name);
}
private void下载文件(string[]目标、string sub\u id、string base\u url)
{
var tasks=新列表();
int c=0;
foreach(目标中的var目标)
{
if(target.EndsWith(“.vtt”,StringComparison.InvariantCultureInogoreCase))
{
使用(var wc=new WebClient())
{
var任务=下载文件(WC,BaseIURUR+子II+)/“+目标,子II+”,“+C++ +”,VTT“”;
任务。添加(任务);
}
}
}
Task.WaitAll(tasks.ToArray());
}
私有任务下载文件(WebClient wc、字符串目标、字符串名称)
{
wc.DownloadProgressChanged+=(对象发送方,DownloadProgressChangedEventArgs e)=>
{
Console.WriteLine(e.ProgressPercentage+“%download”);
};
wc.DownloadFileCompleted+=(对象发送方,AsyncCompletedEventArgs e)=>
{
Console.WriteLine(目标+“已下载”);
};
返回wc.DownloadFileTaskAsync(target,Environment.CurrentDirectory+“/Subs/”+name);
}
tl;博士 您将希望
等待
任务和
异步
方法

await Task.WhenAll(tasks.ToArray());
// ...
return await wc.DownloadFileTaskAsync(...);
这假设一个方法具有
async
签名

好吧,但是为什么呢? 抛开这些void方法是事件处理程序的可能性不谈——在winforms、webforms、wpf中的UI线程上运行——您的
WaitAll
是一种阻塞方法。通过等待所有这些,您将阻塞当前线程。通过等待所有这些,您就允许它们异步运行

如果这些void方法也在UI线程上运行,那么这就是第二个类似的问题

一个较长的例子
使用System.Linq;
使用System.Threading.Tasks;
/// 
///等待路径列表中的所有文件下载
/// 
公共异步任务下载FileAsync(IWebClient客户端,IEnumerable文件路径)
{
var downloadTasks=文件路径
.Select(f=>DownloadFileAsync(客户端,f))
.ToArray();
//如果有任何失败,我们将返回发生错误的消息。如果您希望让
//这些下载失败,只需将try/catch移到单个文件下载中即可
//方法如下。
试一试{
//等待全部完成
等待任务
.WhenAll(下载任务)
.配置等待(错误);
返回Task.CompletedTask;
}捕获(例外e){
//我只是编了一个例子,找到合适的类型
//在IDE中使用intellisense的结果
返回任务。错误任务(e);
}
}
/// 
///等待单个文件下载
/// 
公共异步任务下载FileTaskAsync(IWebClient客户端,字符串文件路径)
{
//在客户端中设置请求等
变量url=”http://example.com";
返回等待客户
.DownloadFile(url、文件路径)
.配置等待(错误);
}
tl;博士 您将希望
等待
任务和
异步
方法

await Task.WhenAll(tasks.ToArray());
// ...
return await wc.DownloadFileTaskAsync(...);
这假设一个方法具有
async
签名

好吧,但是为什么呢? 抛开这些void方法是事件处理程序的可能性不谈——在winforms、webforms、wpf中的UI线程上运行——您的
WaitAll
是一种阻塞方法。通过等待所有这些,您将阻塞当前线程。通过等待所有这些,您就允许它们异步运行

如果这些void方法也在UI线程上运行,那么这就是第二个类似的问题

一个较长的例子
使用System.Linq;
使用System.Threading.Tasks;
/// 
///等待路径列表中的所有文件下载
/// 
公共异步任务下载FileAsync(IWebClient客户端,IEnumerable文件路径)
{
var downloadTasks=文件路径
.Select(f=>DownloadFileAsync(客户端,f))
.ToArray();
//如果有任何失败,我们将返回发生错误的消息。如果您希望让
//这些下载失败,只需将try/catch移到单个文件下载中即可
//方法如下。
试一试{
//等待全部完成
等待任务
.WhenAll(下载任务)
.配置等待(错误);
返回Task.CompletedTask;
}捕获(例外e){
//我只是编了一个例子,找到合适的类型
//在IDE中使用intellisense的结果
返回任务。错误任务(e);
}
}
/// 
///等待单个文件下载
/// 
公共异步任务下载FileTaskAsync(IWebClient客户端,字符串文件路径)
{
//在客户端中设置请求等
变量url=”http://example.com";
返回等待客户
.DownloadFile(url、文件路径)
.配置等待(错误);
}

< /代码> 您应该考虑使用微软的反应框架(AKA RX)- NuGet <代码>系统。代码>-然后您可以执行以下操作:

var query =
    from x in targets.ToObservable().Select((t, c) => new { t, c })
    where x.t.EndsWith(".vtt", StringComparison.InvariantCultureIgnoreCase)
    let target = $"{base_url}{sub_id}/{x.t}"
    let name = $"{sub_id}.{x.c}.vtt"
    from status in
        Observable
            .Using(
                () => new WebClient(),
                wc =>
                {
                    var progress =
                        Observable
                            .FromEventPattern<DownloadProgressChangedEventHandler, DownloadProgressChangedEventArgs>(
                                h => wc.DownloadProgressChanged += h, h => wc.DownloadProgressChanged -= h)
                            .Select(ep => $"{ep.EventArgs.ProgressPercentage}% downloaded.");

                    var completed =
                        Observable
                            .FromAsync(() => wc.DownloadFileTaskAsync(target, $"{Environment.CurrentDirectory}/Subs/{name}"))
                            .Select(z => $"{target} was downloaded.");

                    return progress.Merge(completed);
                })
    select new { target, status };
IDisposable subscription =
    query
        .ObserveOnDispatcher() // or .ObserveOn(instanceOfForm)
        .Subscribe(
            x => Console.WriteLine(x.status),
            ex => { /* handle an exception */ },
            () => { /* when everything is done */ });
但更惯用的处理方式是:

IDisposable subscription =
    query
        .Subscribe(
            x => Console.WriteLine(x.status),
            ex => { /* handle an exception */ },
            () => { /* when everything is done */ });
这将在结果可用时立即对其进行处理,并使您有机会在完成后运行一些代码

如果需要马歇尔到UI线程,则可以执行以下操作:

var query =
    from x in targets.ToObservable().Select((t, c) => new { t, c })
    where x.t.EndsWith(".vtt", StringComparison.InvariantCultureIgnoreCase)
    let target = $"{base_url}{sub_id}/{x.t}"
    let name = $"{sub_id}.{x.c}.vtt"
    from status in
        Observable
            .Using(
                () => new WebClient(),
                wc =>
                {
                    var progress =
                        Observable
                            .FromEventPattern<DownloadProgressChangedEventHandler, DownloadProgressChangedEventArgs>(
                                h => wc.DownloadProgressChanged += h, h => wc.DownloadProgressChanged -= h)
                            .Select(ep => $"{ep.EventArgs.ProgressPercentage}% downloaded.");

                    var completed =
                        Observable
                            .FromAsync(() => wc.DownloadFileTaskAsync(target, $"{Environment.CurrentDirectory}/Subs/{name}"))
                            .Select(z => $"{target} was downloaded.");

                    return progress.Merge(completed);
                })
    select new { target, status };
IDisposable subscription =
    query
        .ObserveOnDispatcher() // or .ObserveOn(instanceOfForm)
        .Subscribe(
            x => Console.WriteLine(x.status),
            ex => { /* handle an exception */ },
            () => { /* when everything is done */ });

如果需要提前停止下载,只需执行
subscription.Dispose() > 

< P>您应该考虑使用微软的反应框架(AKA RX)- NuGet <代码>系统。代码>-然后您可以执行以下操作:

var query =
    from x in targets.ToObservable().Select((t, c) => new { t, c })
    where x.t.EndsWith(".vtt", StringComparison.InvariantCultureIgnoreCase)
    let target = $"{base_url}{sub_id}/{x.t}"
    let name = $"{sub_id}.{x.c}.vtt"
    from status in
        Observable
            .Using(
                () => new WebClient(),
                wc =>
                {
                    var progress =
                        Observable
                            .FromEventPattern<DownloadProgressChangedEventHandler, DownloadProgressChangedEventArgs>(
                                h => wc.DownloadProgressChanged += h, h => wc.DownloadProgressChanged -= h)
                            .Select(ep => $"{ep.EventArgs.ProgressPercentage}% downloaded.");

                    var completed =
                        Observable
                            .FromAsync(() => wc.DownloadFileTaskAsync(target, $"{Environment.CurrentDirectory}/Subs/{name}"))
                            .Select(z => $"{target} was downloaded.");

                    return progress.Merge(completed);
                })
    select new { target, status };
IDisposable subscription =
    query
        .ObserveOnDispatcher() // or .ObserveOn(instanceOfForm)
        .Subscribe(
            x => Console.WriteLine(x.status),
            ex => { /* handle an exception */ },
            () => { /* when everything is done */ });
但更惯用的处理方式是:

IDisposable subscription =
    query
        .Subscribe(
            x => Console.WriteLine(x.status),
            ex => { /* handle an exception */ },
            () => { /* when everything is done */ });
这将在结果可用时立即对其进行处理,并使您有机会在完成后运行一些代码

如果需要马歇尔到UI线程,则可以执行以下操作:

var query =
    from x in targets.ToObservable().Select((t, c) => new { t, c })
    where x.t.EndsWith(".vtt", StringComparison.InvariantCultureIgnoreCase)
    let target = $"{base_url}{sub_id}/{x.t}"
    let name = $"{sub_id}.{x.c}.vtt"
    from status in
        Observable
            .Using(
                () => new WebClient(),
                wc =>
                {
                    var progress =
                        Observable
                            .FromEventPattern<DownloadProgressChangedEventHandler, DownloadProgressChangedEventArgs>(
                                h => wc.DownloadProgressChanged += h, h => wc.DownloadProgressChanged -= h)
                            .Select(ep => $"{ep.EventArgs.ProgressPercentage}% downloaded.");

                    var completed =
                        Observable
                            .FromAsync(() => wc.DownloadFileTaskAsync(target, $"{Environment.CurrentDirectory}/Subs/{name}"))
                            .Select(z => $"{target} was downloaded.");

                    return progress.Merge(completed);
                })
    select new { target, status };
IDisposable subscription =
    query
        .ObserveOnDispatcher() // or .ObserveOn(instanceOfForm)
        .Subscribe(
            x => Console.WriteLine(x.status),
            ex => { /* handle an exception */ },
            () => { /* when everything is done */ });
阻止道琼斯指数下跌