【问题标题】:Task.StartNew Parallel.ForEach doesn't awaitTask.StartNew Parallel.ForEach 不等待
【发布时间】:2016-01-18 14:43:58
【问题描述】:

我有这个代码:

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 个元素的列表,但它不会等待下载和文件写入完成。 事实上,他开始下载和写作,显示消息,然后在后台继续下载...... 我需要并行和异步下载很多文件,但我必须等待所有文件都下载完毕。这段代码有什么问题?

【问题讨论】:

    标签: c# .net async-await task-parallel-library


    【解决方案1】:

    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 这是一个 nuget,不是“传统”.net 框架的一部分。你可以在这里得到它:nuget.org/packages/Microsoft.Tpl.Dataflow
    • 好的,现在它运行了,但它仍然不等待一切都完成
    • @giogiowefj 您需要等待块使用其Completion 属性完成,正如您在我的回答中看到的那样。
    • 哦,没看到,你编辑答案了吗?有了它,它就可以正常工作了!太感谢了!但我仍然不明白为什么我不能使用我的代码。我这辈子都没见过 ActionBlock,在哪里可以学习?
    猜你喜欢
    • 1970-01-01
    • 2015-08-19
    • 2013-04-04
    • 1970-01-01
    • 2020-10-07
    • 1970-01-01
    • 2023-01-27
    • 2018-12-09
    • 1970-01-01
    相关资源
    最近更新 更多