我正在尝试运行一些下载多个文件的代码,并等待所有代码完成后再继续执行代码。
我当前正在运行这部分代码,它实际上确实按照预期的方式下载了所有内容,但不会恢复执行。程序只是冻结了。
如果您能帮助我解决这个问题,那就太好了。
multiprocessing.Pool().starmap()
答案 0 :(得分:1)
您将要await
个任务和async
个方法
await Task.WhenAll(tasks.ToArray());
// ...
return await wc.DownloadFileTaskAsync(...);
这假定签名为async
的方法。
不考虑这些void方法是事件处理程序的可能性-在winforms,webforms,wpf等UI线程上运行-您的WaitAll
是 blocking 方法。通过等待所有这些,您正在阻塞当前线程。通过等待所有这些,您就可以允许它们异步运行。
如果这些无效方法也正在UI线程上运行,那将是第二个类似的问题。
using System.Linq;
using System.Threading.Tasks;
/// <summary>
/// Await all files in a list of paths to download
/// </summary>
public async Task DownloadFilesAsync(IWebClient client, IEnumerable<string> filePaths)
{
var downloadTasks = filePaths
.Select(f => DownloadFileAsync(client, f))
.ToArray();
// if any fails, we return that an error occurred. If you prefer to let "some" of
// those downloads fail, just move the try/catch into the individual file download
// method below.
try {
// wait for all to complete
await Task
.WhenAll(downloadTasks)
.ConfigureAwait(false);
return Task.CompletedTask;
} catch (Exception e) {
// I just made this up as an example; find the right type
// of result using intellisense in your IDE
return Task.ErroredTask(e);
}
}
/// <summary>
/// Await a single file download
/// </summary>
public async Task DownloadFileTaskAsync(IWebClient client, string filePath)
{
// set up request in the client, etc
var url = "http://example.com";
return await client
.DownloadFile(url, filePath)
.ConfigureAwait(false);
}
答案 1 :(得分:0)
您应该考虑使用Microsoft的Reactive Framework(aka Rx)-NuGet System.Reactive
并添加using System.Reactive.Linq;
-然后您可以执行以下操作:
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 };
这是一个异步查询,它处理所有并行调用-在完成每个调用时处置WebClient
。
您可以等待所有结果,如下所示:
query
.Do(x => Console.WriteLine(x.status))
.ToArray()
.Wait();
但是更惯用的处理方式是:
IDisposable subscription =
query
.Subscribe(
x => Console.WriteLine(x.status),
ex => { /* handle an exception */ },
() => { /* when everything is done */ });
这将尽快处理结果,并在完成后为您提供运行一些代码的机会。
如果需要编组到UI线程,则可以执行以下操作:
IDisposable subscription =
query
.ObserveOnDispatcher() // or .ObserveOn(instanceOfForm)
.Subscribe(
x => Console.WriteLine(x.status),
ex => { /* handle an exception */ },
() => { /* when everything is done */ });
要在需要提早停止的情况下停止下载,只需执行subscription.Dispose();
。