如何正确调用 Parallel.ForEach 循环中的调用异步方法[重复]

Posted

技术标签:

【中文标题】如何正确调用 Parallel.ForEach 循环中的调用异步方法[重复]【英文标题】:how to call Call Async Methods in Parallel.ForEach loop properly [duplicate] 【发布时间】:2020-09-09 00:59:21 【问题描述】:

如何正确调用 Parallel.ForEach 循环中的调用异步方法。billDataService.SaveBillDetail 和 GetProfileDetails 都是异步方法。数据保存在 MongoDB 中的 SaveBillDetail 中。

public async Task ConvertXMLFileToJSON()
        
            try
            

                if (Directory.Exists(_appSettings.DocumentsStorage))
                
                    int i = 1; //Test
                    bool exists = Directory.GetFiles(_appSettings.DocumentsStorage).Any(x => x.Equals(Path.Combine(_appSettings.DocumentsStorage, "Ready.txt"), StringComparison.OrdinalIgnoreCase)); //Need to check
                    if (exists)
                    
                      Parallel.ForEach(System.IO.Directory.GetFiles(_appSettings.DocumentsStorage, "*.xml"), (currentFile) =>
                        
                            try
                            
                                XElement root = XElement.Load(currentFile); // or .Parse(string);
                                //Removing CDATA property from XElement.
                                XElement items = XElement.Parse(root.ToString().Replace("<![CDATA", "").Replace("]]>", "").Replace("[", ""));
                                //Removing XML_INFO Tag from XElement.
                                items.Elements("XML_INFO").Remove();
                                XmlDocument xmlDoc = new XmlDocument();
                                using (XmlReader xmlReader = items.CreateReader())
                                
                                    xmlDoc.Load(xmlReader);
                                

                                var json = JsonConvert.SerializeXmlNode(xmlDoc);
                                billDetails obj = JsonConvert.DeserializeObject<billDetails>(json);
                                BillDetails billDetails = new BillDetails();
                                billDetails.AccountNumber = obj.BILL_INFO.BILL_RUN.ACCT_INFO.ACCT_CODE;
                                billDetails.MobileNumber = obj.BILL_INFO.BILL_RUN.ACCT_INFO.PRINCIPAL_NO.STR_PRINCIPAL_NO;
                                billDetails.BillDate = DateTime.ParseExact(obj.BILL_INFO.BILL_RUN.BILL_PROP.TO_DATE, "dd/MM/yyyy", CultureInfo.InvariantCulture);
                                billDetails.DueAmount = obj.BILL_INFO.BILL_RUN.ACCT_INFO.ACCT_BALANCE_TRACE.TOTAL_DUE;
                                billDetails.CustomerName = obj.BILL_INFO.BILL_RUN.CUST_INFO.CUST_NAME.FULL_NAME;
                                billDetails.InvoiceId = obj.BILL_INFO.BILL_RUN.BILL_PROP.INVOICE_ID;
                                billDetails.DueDate = DateTime.ParseExact(obj.BILL_INFO.BILL_RUN.BILL_PROP.DUE_DATE, "yyyyMMdd hh:mm:ss", CultureInfo.InvariantCulture);
                                billDetails.RepositoryName = "postpaid";
                                billDetails.BillRun = obj.BILL_INFO.BILL_RUN; //tempObj2.BILL_INFO.ToString().Remove(0, 1);
                                billDetails.ObjectId = Guid.NewGuid().ToString();
                                if (billDetails != null)
                                
                                    BillDataService billDataService = new BillDataService(_dbConfig);
                                    Console.WriteLine("SaveBillDetail");
                                    if (billDataService.SaveBillDetail(billDetails) != null)
                                    
                                        Console.WriteLine("SaveBillDetail done");
                                        GetProfileDetails(billDetails);
                                        _logger?.LogInformation(i++ + "  File Success");
                                        Console.WriteLine(i++ + "  File Success");
                                        // File.Delete(file);  //Delete File 
                                    
                                

                            
                            catch (Exception ex)
                            
                                _logger?.LogError(ex, "Error");
                            
                            finally  
                        );
                    
                
            

            catch (Exception ex)
            
                _logger?.LogError(ex, "Error");
            
            finally
            

            
        


public async Task GetProfileDetails(BillDetails billDetails)
        
            try
            
                ProfileService profileService = new ProfileService(_dbConfig);
                var searchFilter = new SearchFilter
                
                    Filters = new List<Filter>()
                ;

                if (!string.IsNullOrEmpty(billDetails.AccountNumber))
                
                    searchFilter.Filters.Add(new Filter()  PropertyName = "AccountNumber", Operator = Operator.Equals, Value = billDetails.AccountNumber, CaseSensitive = true );
                

                if (searchFilter != null)
                
                    Profile profile = await profileService.GetProfiles(searchFilter);
                    if (profile != null)
                    
                        await SendMailNotification(profile, billDetails);
                    
                    else
                    
                        _logger?.LogError("Profile Info not found");

                    
                
            
            catch (Exception ex)
            
                _logger?.LogError(ex, "Error");
                throw;
            
            finally  
        


正常的每个循环我可以在 MongoDB.but Parallel.ForEach 循环中调用和保存数据调用方法前面的await。

【问题讨论】:

【参考方案1】:

您可以将代码更改为如下所示,然后等待所有任务完成。

var fileTasks = System.IO.Directory.GetFiles(_appSettings.DocumentsStorage, "*.xml").Select(async currentFile =>
                        
                            try
                            
                                XElement root = XElement.Load(currentFile); // or .Parse(string);
                                // rest o your code here
                                if (billDetails != null)
                                
                                    if (billDataService.SaveBillDetail(billDetails) != null)
                                    
                                        Console.WriteLine("SaveBillDetail done");
                                        await GetProfileDetails(billDetails);
                                    
                                
                             
                          catch(exception ex)  //log exeption 
                       );

await Task.WhenAll(fileTasks);

【讨论】:

它正在工作,但没有变化。每个循环的性能与正常情况相比是相同的。

以上是关于如何正确调用 Parallel.ForEach 循环中的调用异步方法[重复]的主要内容,如果未能解决你的问题,请参考以下文章

提高性能异步 Parallel.Foreach

如何从 Parallel.ForEach 收集返回值?

计算 Parallel.ForEach 使用的线程数

C# - 用于服务调用的 Parallel.Foreach()

如何通过 Parallel.ForEach 实现最大并行度并利用最大 CPU?

C#控制台调用rest API 100k plus多处理模式(Parallel.ForEach)[重复]