平行的.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;
    }

平行的.ForEach在循环中不设置所有值

  1. 你们对BuildUserFilePath的定义和用法不一致。定义清楚地表明它是一个返回string的方法,而它的用法意味着它返回一个Task<>
  2. 平行。ForEach和async不能很好地混合-这就是你的bug发生的原因。
  3. 无关,但仍然值得注意:你的try/catch是多余的,因为它所做的只是重新抛出原来的SqlException(甚至它做得不太好,因为你最终会失去堆栈跟踪)。
  4. 你真的真的想要返回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()会得到什么呢?如果没有它,代码肯定会更容易理解。

我不清楚你想达到什么目的。你能描述一下这种方法的目的吗?