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 */ });
阻止道琼斯指数下跌