使用 Parallel.For 汇总 C# 数据表列

本文关键字:数据表 汇总 Parallel For 使用 | 更新日期: 2023-09-27 17:56:39

我有这个数据表:

DataTable dt = GetDatatTable();

其中一列是Amount(十进制)

我想用TPL尽快总结它。

  object obj  = new Object();
  var total=0m;
  Parallel.For (1, dt.Rows.Count+1  ,i => {lock (obj) total += Decimal.Parse(dt.Rows[i-1]["Amount"]) });

但我真的不想锁很多次。

问题 #1

还有其他选择可以减少广泛的锁吗?

问题 #2

我不明白为什么我需要保护总蓄能器

  • 保护是针对+=还是针对多线程更新total

    我的意思是看看下面的流程,一个Volatile字段可以轻松解决它。

    假设 total=0
    并且数据表项1,2,3

    1) 第一个线程:总计 = 总计+1。(总计=1)

    2)第二个线程:总计=总计+___stop__ (上下文切换,线程3的值为3) ___val=_3____(总计=1+3=4)

    3) 上下文切换回线程 2 总计 = 4+2 = 6。

    所以一切似乎都很好.

我一定在这里错过了什么。

附言我知道我可以用:

ParallelEnumerable.Range (1, dt.Rows.Count+1).Sum (i => Decimal.Parse(dt.Rows[i-1]["Amount"]) )

但我想学会用Parallel.For

使用 Parallel.For 汇总 C# 数据表列

是的,有减少锁的替代方法:

  1. 使用支持本地数据的Parallel.For()重载。这样,您只需要在localFinally委托中进行同步(但您不应该忘记它)。
  2. 使用Interlocked.Add() .这在您的情况下不起作用,因为重载仅适用于intlong,不适用于decimal
  3. 不要使用并行处理。通过像这样非常简单的操作,并行处理的开销很可能超过速度的提高。
  4. 使用 PLINQ:

    var total =
        ParallelEnumerable.Range(0, dt.Rows.Count)
                          .Select(i => Decimal.Parse(dt.Rows[i]["Amount"]))
                          .Sum();
    

关于您的线程安全问题,您假设在"上下文切换"之后(我使用可怕的引号,因为在多核 CPU 上,不必发生任何上下文切换),线程将再次读取当前值total。但实际上,它已经读取了旧值,现在保存在寄存器中。因此,步骤 3 中的结果将变为 1 + 2 = 3。

由于您需要使用锁定来确保正确的结果,因此我认为Parallel.For不会给您买任何东西。您不能并行锁定某些内容;根据定义,锁定是串联完成的。

因此,一个简单的for循环将同样具有性能,并且更易于使用。