1

我正在查询一些员工的 sql 数据库。当我收到这些员工时,我会使用 Parallel.ForEach 循环每个员工。

我循环从数据库中检索到的员工的唯一原因是扩展了一些我不想弄乱数据库的属性。

在这个例子中,我试图在循环中为当前员工设置 Avatar,但始终只有三分之一的员工被设置,其他员工 Avatar 都没有被设置为正确的 URI。基本上,我使用头像的文件名并构建用户文件夹的完整路径。

我在这里做错了什么,每个员工的头像都没有更新到他们目录的完整路径,就像唯一一个正在设置的?并行堆栈和深四

我确定我的代码格式不正确。我看过那个并行任务,它确实在 6 个线程上创建了 4 个并行任务。

有人可以向我指出格式化代码以使用 Parallel 的正确方法吗?

另外,有一件事,如果我return await Task.Run()=>从 GetEmployees 方法中删除,我会收到无法完成任务的错误,因为其他一些任务首先被钓鱼。

Parallel 的行为就好像它只是为一名员工设置一个 Avatars。

- -呼叫者

   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;
    }
4

2 回答 2

2
  1. 您的定义BuildUserFilePath及其用法不一致。定义清楚地表明它是一个string-returning 方法,而它的用法意味着它返回一个Task<>.
  2. Parallel.ForEach 和 async 不能很好地混合 - 这就是你的错误首先发生的原因。
  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);
                });
            });
        }
    }
    
于 2013-06-08T03:51:10.613 回答
1

这段代码中似乎过度使用了 async 和 Task.Run() 。例如,您希望从这个细分市场中获得什么?

  Parallel.ForEach<uspGetEmployees_Result>(
                             emps,
                            async (e) =>
                            {
                                e.Avatar = await Task.Run(() => BuildUserFilePath(e.Avatar, e.UserId, httpCurrent, true));
                            }
                         );

您已经在整个方法的结果上使用了 await,并且您已经使用 aParallel.ForEach来并行执行循环中的项目,那么额外的用途对await Task.Run()您有什么帮助?没有它,代码肯定会更容易理解。

我不清楚你想在这里实现什么。你能描述一下你对这个方法的目标是什么吗?

于 2013-06-08T02:26:11.207 回答