平行的.ForEach在循环中不设置所有值
本文关键字:设置 循环 ForEach | 更新日期: 2023-09-27 18:17:41
我正在为一些员工查询sql数据库。当我收到这些员工时,我使用Parallel.ForEach循环每个员工。
我循环从数据库中检索到的雇员的唯一原因是扩展了一些属性,我不想让数据库变得混乱。
尽管如此,在本例中,我试图为循环中的当前员工设置Avatar,但始终只有三个员工中的一个被设置,其他员工的Avatar都没有被设置为正确的URI。基本上,我将获取头像的文件名并构建到users文件夹的完整路径。
我在这里做错了什么,每个员工的Avatar没有更新到他们的目录的完整路径,就像唯一一个正在设置的?并行栈和有在深四
我肯定我的代码格式不正确。我看过并行任务,它确实在6个线程上深度创建了4个并行任务。
有人能指出给我正确的方式来格式化代码使用并行?
还有一件事,如果我从GetEmployees方法中删除return await Task.Run()=>
,我会得到一个无法完成任务的错误,因为其他任务先完成。
Parallel的行为就好像它只为其中一个雇员设置了一个avatar。
调用者——
public async static Task<List<uspGetEmployees_Result>> GetEmployess(int professionalID, int startIndex, int pageSize, string where, string equals)
{
var httpCurrent = HttpContext.Current;
return await Task.Run(() =>
{
List<uspGetEmployees_Result> emps = null;
try
{
using (AFCCInc_ComEntities db = new AFCCInc_ComEntities())
{
var tempEmps = db.uspGetEmployees(professionalID, startIndex, pageSize, where, equals);
if (tempEmps != null)
{
emps = tempEmps.ToList<uspGetEmployees_Result>();
Parallel.ForEach<uspGetEmployees_Result>(
emps,
async (e) =>
{
e.Avatar = await Task.Run(() => BuildUserFilePath(e.Avatar, e.UserId, httpCurrent, true));
}
);
};
}
}
catch (SqlException ex)
{
throw ex;
};
return emps;
});
}
——被
static string BuildUserFilePath(object fileName, object userProviderKey, HttpContext context, bool resolveForClient = false)
{
return string.Format("{0}/{1}/{2}",
resolveForClient ?
context.Request.Url.AbsoluteUri.Replace(context.Request.Url.PathAndQuery, "") : "~",
_membersFolderPath + AFCCIncSecurity.Folder.GetEncryptNameForSiteMemberFolder(userProviderKey.ToString(), _cryptPassword),
fileName.ToString());
}
---------------------------------------- 编辑 ------------------------------------
我在大家的帮助下使用的最后代码。非常感谢!
public async static Task<List<uspGetEmployees_Result>> GetEmployess(int professionalID, int startIndex, int pageSize, string where, string equals)
{
var httpCurrent = HttpContext.Current;
List<uspGetEmployees_Result> emps = null;
using (AFCCInc_ComEntities db = new AFCCInc_ComEntities())
{
emps = await Task.Run(() => (db.uspGetEmployees(professionalID, startIndex, pageSize, where, equals) ?? Enumerable.Empty<uspGetEmployees_Result>()).ToList());
if (emps.Count() == 0) { return null; }
int skip = 0;
while (true)
{
// Do parallel processing in "waves".
var tasks = emps
.Take(Environment.ProcessorCount)
.Select(e => Task.Run(() => e.Avatar = BuildUserFilePath(e.Avatar, e.UserId, httpCurrent, true))) // No await here - we just want the tasks.
.Skip(skip)
.ToArray();
if (tasks.Length == 0) { break; }
skip += Environment.ProcessorCount;
await Task.WhenAll(tasks);
};
}
return emps;
}
- 你们对
BuildUserFilePath
的定义和用法不一致。定义清楚地表明它是一个返回string
的方法,而它的用法意味着它返回一个Task<>
。 - 平行。ForEach和async不能很好地混合-这就是你的bug发生的原因。
- 无关,但仍然值得注意:你的
try/catch
是多余的,因为它所做的只是重新抛出原来的SqlException
(甚至它做得不太好,因为你最终会失去堆栈跟踪)。 -
你真的真的想要返回
null
吗?public async static Task<List<uspGetEmployees_Result>> GetEmployess(int professionalID, int startIndex, int pageSize, string where, string equals) { var httpCurrent = HttpContext.Current; // Most of these operations are unlikely to be time-consuming, // so why await the whole thing? using (AFCCInc_ComEntities db = new AFCCInc_ComEntities()) { // I don't really know what exactly uspGetEmployees returns // and, if it's an IEnumerable, whether it yields its elements lazily. // The fact that it can be null, however, bothers me, so I'll sidestep it. List<uspGetEmployees_Result> emps = await Task.Run(() => (db.uspGetEmployees(professionalID, startIndex, pageSize, where, equals) ?? Enumerable.Empty<uspGetEmployees_Result>()).ToList() ); // I'm assuming that BuildUserFilePath returns string - no async. await Task.Run(() => { Parallel.ForEach(emps, e => { // NO async/await within the ForEach delegate body. e.Avatar = BuildUserFilePath(e.Avatar, e.UserId, httpCurrent, true); }); }); } }
这段代码中似乎过度使用了async和Task.Run()。例如,你希望从这个细分市场中获得什么?
Parallel.ForEach<uspGetEmployees_Result>(
emps,
async (e) =>
{
e.Avatar = await Task.Run(() => BuildUserFilePath(e.Avatar, e.UserId, httpCurrent, true));
}
);
你已经对整个方法的结果使用了await,并且你已经使用了Parallel.ForEach
来并行执行循环中的项目,那么额外使用await Task.Run()
会得到什么呢?如果没有它,代码肯定会更容易理解。