我在 C# 中使用并行 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; }
}
您需要首先简化它并消除重复的并发。 这可能只是并行和任务之间的一些不兼容。
首先尝试类似的操作,然后让我们知道您是否仍然在最终列表中得到空值。
注意这并不能保证是有效的代码 - 我只是在在线 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;}