Task.StartNew与Parallel.ForEach不等待

4

我有这段代码:

await Task.Factory.StartNew(
    () => Parallel.ForEach(
        urls,
        new ParallelOptions { MaxDegreeOfParallelism = 2 },
        async url =>
        {
           Uri uri = new Uri(url);
           string filename = System.IO.Path.GetFileName(uri.LocalPath);

           using (HttpClient client = new HttpClient())
           using (HttpResponseMessage response = await client.GetAsync(url))
           using (HttpContent content = response.Content)
           {
               // ... Read the string.
               using (var fileStream = new FileStream(config.M_F_P + filename, FileMode.Create, FileAccess.Write))
               {
                   await content.CopyToAsync(fileStream);
               }
           }
        }));

MessageBox.Show("Completed");

这段代码应该可以处理超过800个元素的列表,但它没有等待下载和文件写入完成。实际上,它开始下载和写入,显示消息,然后在后台继续下载...我需要并行异步地下载许多文件,但我必须等待它们全部下载完毕。这段代码有什么问题吗?

1个回答

6

Parallel.ForEach不支持异步操作。它期望一个Action,但为了等待异步方法,它需要得到一个Func<Task>

你可以使用TPL Dataflow的ActionBlock,它是专门为异步构建的。你提供一个委托(异步或非异步)来对每个项执行操作。你配置块的并行性(如果需要,还可以限制容量)。然后将你的项发布到其中:

var block = new ActionBlock<string>(async url => 
{
    Uri uri = new Uri(url);
    string filename = System.IO.Path.GetFileName(uri.LocalPath);

    using (HttpClient client = new HttpClient())
    using (HttpResponseMessage response = await client.GetAsync(url))
    using (HttpContent content = response.Content)
    {
       // ... Read the string.
       using (var fileStream = new FileStream(config.M_F_P + filename, FileMode.Create, FileAccess.Write))
       {
           await content.CopyToAsync(fileStream);
       }
    }
}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 } );

foreach (var url in urls)
{
    block.Post(url);
}

block.Complete();
await block.Completion;
// done

它说此命名空间中不存在ActionBlock。如果我尝试使用System.Threading.Tasks.Dataflow导入它,则会显示Dataflow不存在。我该怎么办? - giogiowefj
@giogiowefj 这是一个NuGet包,不是“传统”的.NET框架的一部分。您可以在此处获取它:https://www.nuget.org/packages/Microsoft.Tpl.Dataflow/ - i3arnon
好的,现在它可以运行了,但它仍然不会等待所有事情完成。 - giogiowefj
@i3arnon 我读到过new一个新的 HttpClient 并不是好的做法,每次. 所以也许应该将其移到异步操作之外? - Pure.Krome
@Pure.Krome 这只是 OP 行动的复制... 但是,创建可以被提取出来。 - i3arnon
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接