【问题标题】:Write an async method with Parallel.Foreach loop that does call another async method to pull record使用 Parallel.Foreach 循环编写一个异步方法,该循环调用另一个异步方法来提取记录
【发布时间】:2022-08-16 02:36:23
【问题描述】:

我正在进行代码性能优化,需要建议使用parallel.foreach 和/或WhenAll 实现async 的最佳方法。

代码分为三个主要区域。

Code Definition

MethodA 提取Customer 列表

MethodBPartA 循环遍历客户并通过Azure Function 从数据库中选择记录。这是 1:* 关系,因此 1 个客户可以有多个记录。

MethodB PartB 查看在方法 B 部分 A 中选择的客户记录列表,看看是否有任何附加文件。如果有文件/文件,则处理并将“客户参考”发送回“方法A”,将记录存储在字典中。然后它发送

方法A

public async Task<List<Customers>> MethodA(){

  List<Customer> customers = await GetAllCustomers();
  var inboundCustomerFiles= new List<InboundCustomerFiles>(); 

   Parallel.ForEach(customer, async customer =>
   {
     var processedCustomer = await MethodB(customer);
     inboundCustomersFiles.AddRange(processedCustomer);

   });
}

方法B

  public static async Task<List<InboundCustomerFiles>> MethodB(Customer customer){
     var customerRecord = await GetCustomerRecord(customerId);

     foreach(var customer in customerRecord){
        var files = await getCustomerRecordFile(customerRecordId)
        //...... remaining code
     }
    return inboundCustomerFiles;
  }

方法三

public static async Task<List<InboundCustomerFiles>> GetCustomerRecord(int customerId){
     //API call here that further pull `record` from database
return List<Records>();
}

methodB customerRecord 中的过程需要时间。我如何确保它处理数据并返回到 MethodA 中的正确客户线程。我曾尝试在 methodB 中使用,但它变慢了,进一步我知道 Parallel.Foreach 不会等待,所以我尝试在 lambda 表达式中添加 async 引用,但不确定 A 是否正确或是否有效。

  • Parallel.ForEachis not async-friendly
  • 你能在上面的客户场景中帮助我吗,我需要一些指导……非常感谢
  • 我想运行Customer 多个实例,我应该使用什么来代替Parallel>forEach?
  • 您已经解释了客户和记录之间的关系(一对多),但是您还没有解释这两个实体和“文件”实体之间的关系。文件是与客户相关还是与记录相关?以及它们之间的关系是什么?
  • 该文件与record 相关并且是1:* 关系,因此1 条记录可以有0 个或多个文件

标签: c# async-await parallel.foreach


【解决方案1】:

一方面,您可以假装Parallel.ForEach 等待您的async 函数,但事实并非如此。相反,你想写这样的东西:

   await Task.WhenAll(customers.Select(async customer =>
   {
     var processedCustomer = await MethodB(customer);
     inboundCustomersFiles.AddRange(processedCustomer);
   }));

Task.WhenAll 的行为类似于 Parallel.ForEach,但它是可等待的,并且它还在完成自己的任务之前等待您传递给它的每个任务。因此,当您的await Task.WhenAll 完成时,所有内部任务也已完全完成。

methodB customerRecord 中的过程需要时间

这是非常模棱两可的。如果您的意思是它需要服务器和/或 IO 时间,那很好,这就是 async 的用途。如果你的意思是需要您的CPU时间(即它在本地处理数据很长时间),那么你应该在线程池上启动一个任务并等待它的完成。请注意,这不一定是默认线程池!特别是如果您正在编写一个 ASP.NET(核心)应用程序,您需要一个专门用于这些东西的专用线程池。

【讨论】:

  • 两者都批量处理大量数据,所以你应该没问题!
【解决方案2】:

如果传递给并行 foreach 或 Task.WhenAll 的列表非常大,会发生什么? 我更喜欢使用 foreach 并在数组中添加一个 every taks 并一次执行一个最大数量。

foreach(var customer in customers) {
        var t = getCustomerRecordFile(customerRecordId);

        work.Add(t);

        if(work.Count >= maxTaksAtTime ){
        await Task.WhenAll(work);
        inboundFiles.AddRange(work.Select(t => t.Result));
        }
    }
    if(work.Count >= 1)// ... wait to finish registerd task

您如何处理大量任务?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-29
    • 2011-01-26
    • 1970-01-01
    • 2014-09-13
    • 2020-09-09
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多