从 API 获取数据时,C# Parallel.ForEach 循环中的员工详细信息变为 null

问题描述 投票:0回答:1

我在 C# 中使用并行 foreach 循环从 API 获取员工详细信息时遇到了问题。请注意,我对任务使用了并发包,以使其线程安全,并将项目添加到锁内的列表中,以避免它同时被多个线程访问

这就是我正在做的:

  • 我有一份员工编号列表。
  • 我使用并行 foreach 循环迭代此列表。
  • 在循环内,我使用当前员工编号调用 API 来检索员工详细信息。
  • 我将获取的员工详细信息添加到列表中。

但是,我注意到有时员工详细信息列表中的员工对象会变为空。我不确定为什么会发生这种情况以及如何解决。

public List<ImportedEmployee> GetEmployees(List<String> empNumber)
{
    List<ImportedEmployee> employeeList = new List<ImportedEmployee>();

    try
    {
        List<ImportedEmployee> cb = new List<ImportedEmployee>();
        var tasks = new ConcurrentBag<Task>();

        Parallel.ForEach(empNumber, reference =>
        {
            tasks.Add(Task.Run(() =>
            {
                var importedEmployee = GetEmployeeDetails(reference.ToString());

                if (importedEmployee != null)
                {
                    lock (cb)
                    {
                        cb.Add(importedEmployee);
                    }
                }
            }));
        });

        Task.WaitAll(tasks.Where(t => t != null).ToArray());

        employeeList.AddRange(cb.ToList());
    }
    catch (Exception ex)
    {
        Log.Error("Error");
    }

    return employeeList;
}

public ImportedEmployee GetEmployeeDetails(string empNum)
{
    ImportedEmployee employeeDetails = new ImportedEmployee();

    try
    {
        var responseString = ServiceRequest($"Employees/{empNum}", null, HttpVerbs.Get).ToString();

        JObject result = JObject.Parse(responseString);
        employeeDetails.FirstName = result["FirstName"].ToString();
        employeeDetails.LastName = result["LastName"].ToString();
        employeeDetails.Username = result["LoginId"].ToString();
    }
    catch (Exception ex)
    {
        Log.Error("Error retrieving employee details.", ex);
    }

    return employeeDetails;
}
        public string ServiceRequest(string URI, string PostJSON, HttpVerbs Method)
        {
            string responseString;
            try
            {                
                HttpResponseMessage resp;
                using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(Configuration.ClientTimeout) })
                {
                    HttpRequestMessage request = new HttpRequestMessage();
                    switch (Method)
                    {
                        case HttpVerbs.Get:
                            request = new HttpRequestMessage(HttpMethod.Get, BaseURL + URI);
                            break;
                        default:
                            request = new HttpRequestMessage(HttpMethod.Post, BaseURL + URI);
                            request.Content = new StringContent(PostJSON, null, "application/json");
                            break;
                    }
                    request.Headers.Add("Authorization", $"Bearer {AuthToken.Token}");
                    resp = client.SendAsync(request).Result;
                }
                responseString = resp.Content.ReadAsStringAsync().Result;
            }
            catch (Exception ex)
            {
                Log.Error("Error", ex);
                responseString = null;
            }
            return responseString;
        }

public class ImportedEmployee
    {
        public string EmployeeNumber { get; set; }
        public string Username { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
}
c# task-parallel-library parallel.foreach
1个回答
0
投票

您需要首先简化它并消除重复的并发。 这可能只是并行和任务之间的一些不兼容。

首先尝试类似的操作,然后让我们知道您是否仍然在最终列表中得到空值。

注意这并不能保证是有效的代码 - 我只是在在线 IDE 中执行此操作以向您展示我们正在讨论的内容,因此请纠正任何疏忽。

这是 AsParallel & PLINQ 的建议。

public List<ImportedEmployee> GetEmployees(List<String> empNumber)
{
    try
    {
      return empNumber.AsParallel()                   // parallelize
                    .Select(GetEmployeeDetails)     // call API
                    .OfType<ImportedEmployee>()     // filter out nulls
                    .ToList();                       
   }
   catch (Exception ex)
   {
       Log.Error("Error: " + ex.Message);
       return new List<ImportedEmployee>();
   }
}

这就是不使用 Parallel.ForEach 的代码的样子。

public List<ImportedEmployee> GetEmployees(List<String> empNumber)
{
    var employees = new List<ImportedEmployee>();

try
{
    Parallel.ForEach(empNumber, reference =>
    {
            var importedEmployee = GetEmployeeDetails(reference.ToString());

            if (importedEmployee != null)
            {
                lock (cb)
                {
                    cb.Add(importedEmployee);
                }
            }
    });

}
catch (Exception ex)
{
    Log.Error("Error: " + ex.Message);
}

return employeeList;}
© www.soinside.com 2019 - 2024. All rights reserved.