【问题标题】:Parallel.ForEach working too slow for DataTableParallel.ForEach 对 DataTable 工作太慢
【发布时间】:2015-07-01 03:05:45
【问题描述】:

我在 DTable 中实现 Parallel.ForEach 时遇到问题。以下是我正在实现的示例代码:

Parallel.ForEach(dTable4.AsEnumerable(), row4 =>
            {
                string getCondition = row4[1].ToString();
                getCondition = EvaluateCondExpression(getCondition);  //Evaluates CM for value (CM1==2 && CM2<5);
                Expression e = new Expression(getCondition); //getCondition = (2==2 && 2<5)
                var evalutateCondition = e.Evaluate();
                if (Convert.ToBoolean(evalutateCondition))
                {
                   //Write string to TextBox
                }
             }

当我运行它时,线程没有得到有效管理,而且它比 foreach 循环花费的时间太长。 从 GUI 组合框和 numericUpDown 检查用户提供的参数后,EvaluateCondExpression 函数返回值

private string EvaluateCondExpression(string getCondition)
    {
        string[] splitgetCondition1 = SeprateCharacter(getCondition);
        foreach (string oneCondition in splitgetCondition1)
        {
            string conditionValue = oneCondition;
            if (oneCondition.Contains("CM"))
            {
                conditionValue = RemoveMultipleChar(conditionValue.Replace('!', ' ').Trim());
                string getInputNumber = getTableOneInputNo(conditionValue, dTable1);
                string tableOneValue = checkComboxValue(m_comboBox, getInputNumber);

                getCondition = getCondition.Replace(conditionValue, tableOneValue);
            }
        }
        return getCondition;
    }

串行 ForEach 计算花费了太多时间,所以我想应用 Parallel.ForEach 迭代,但不幸的是它不起作用。谁能建议我如何最大限度地提高性能以及我在 Parallel.ForEach 中做错了什么。

【问题讨论】:

  • 您可能会看到通过使用传统的 foreach 将每次迭代包装在一个任务中来提高性能。将每次迭代的任务添加到集合中,然后在 foreach 调用之外添加 Task.WhenAll(tasks);,如果有的话,它会让您能够等待昂贵的并行进程。
  • 你能给我提供任何参考链接吗?
  • 参考显示性能改进?或链接显示这样做的例子?
  • 将每个迭代包装在一个任务中的参考。
  • 对于长时间运行的任务,超过 2-3 秒的任务可以考虑使用 Async - Await,而不是 Parallel.ForEach。 Infact Async - Await 甚至不像并行调用那样阻塞

标签: c# foreach datatable parallel-processing parallel.foreach


【解决方案1】:

您可能会看到通过使用传统的 foreach 将每次迭代包装在一个任务中来提高性能。将每次迭代的任务添加到集合中,然后在 foreach 之外调用 Task.WhenAll(tasks);如果有的话,它使您能够等待昂贵的并行过程。

您可以将 Parallel.ForEach 的内容转换为 Select Linq 查询,该查询将每次迭代转换为一个任务。可以将生成的任务集合提供给 Task.WhenAll 方法以等待

await Task.WhenAll(dtable4.AsEnumerable().Select(row4 => new Task(() =>
{
    string getCondition = row4[1].ToString();
    getCondition = EvaluateCondExpression(getCondition);  //Evaluates CM for value (CM1==2 && CM2<5);
    Expression e = new Expression(getCondition); //getCondition = (2==2 && 2<5)
    var evalutateCondition = e.Evaluate();
    if (Convert.ToBoolean(evalutateCondition))
    {
        //Write string to TextBox
    }
})));

这可能无法解决您看到的性能瓶颈,但这至少可以让您等待并行进程并释放 UI 线程。

【讨论】:

  • 这是个好主意,你可以检查并行度是否有帮助,否则这将是一个可行的方法,特别是如果你需要调用非阻塞
  • 他还应该考虑在 WhenAll 完成之前不写入 UI,因此每个任务都不会切换到 UI 同步上下文来写入它。如果您使用任何绑定,则写入 UI 的成本很高,因为必须刷新所有绑定。他没有指定他的 UI 框架,所以 OP 将不得不使用他的判断力
【解决方案2】:

在使用Parallel.ForEach时,可以对代码进行如下修改,尤其是对于长时间运行的任务

Parallel.ForEach( dTable4.AsEnumerable(), new ParallelOptions {MaxDegreeOfParallelism = Environment.ProcessorCount}, row4 = >

这将确保 Parallel.ForEach 不会启动 datatable 中每个数据点的线程,它只会根据环境处理器/内核的数量动态生成线程,从而减少争用和线程上下文切换.然而,理想情况下,如上所述,您可以为长时间运行的任务计划异步模式。

正如我所见,您正在更新并行循环内的 UI 控件,因为无论如何您都需要 UI 线程上下文,否则它将出现异常。无论如何,异步都在 UI 线程上下文中运行

【讨论】:

    【解决方案3】:

    运行一次循环迭代需要多长时间?您正在运行多少次迭代?一般来说,Parallel.Foreach() 非常适合在每次迭代可能需要很长时间的循环中运行。相反,如果您对相对较快的操作进行多次迭代,您将花费大量额外开销来生成和管理线程,这可能就是您所看到的。有关此类方案的更多信息,请参阅此 MSDN 文章:

    https://msdn.microsoft.com/en-us/library/dd560853(v=vs.110).aspx

    更新

    Here 是 Parallel.Foreach() 和 await Task.WhenAll() 的一个很好的比较。

    【讨论】:

    • 迭代大小取决于用户选择参数,但为了测试,我正在考虑第一个 Parallel.ForEach 函数的 7 个循环,在串行 ForEach 操作中运行大约需要 5596 毫秒,而 Parallel.ForEach 大约需要 12879 毫秒执行相同的任务。我想提高性能,因为在处理多次迭代时它会呈指数级滞后。迭代大小可能会进行大约 20~25 个循环,这可能会迫使用户等待超过 15~20 秒。
    • Parallel.Foreach 不适合建议的长时间运行的任务,事实上,如果每个 Parallel 线程都有一个持续时间很长的任务,那么它会导致更多的上下文切换和争用,因为它们会使核心保持忙碌对于更长的持续时间,当其他线程正在尝试它时,Async - Await 是长时间运行任务的更好选择,实际上它甚至不像 Paralel.ForEach 那样阻塞
    • @sk3145 您可能需要花一些时间进行一些基准测试。尝试按照我提供的 MSDN 链接中的示例进行操作。与另一个答案一样,Parallel.ForEach() 有几个选项可用于调整它产生的线程数和其他一些东西,所有这些都可以通过浏览 MSDN 文章找到。如果不尝试不同的东西,很难准确地说出什么使它表现最好。
    猜你喜欢
    • 2022-06-26
    • 1970-01-01
    • 2019-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-27
    • 2019-02-14
    相关资源
    最近更新 更多