用Parallel.For汇总C#Datatable列?

时间:2013-01-29 07:57:47

标签: c# multithreading .net-4.0 parallel-processing task-parallel-library

我有这个DataTable:

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
     DataTable项目为1,2,3

    1)第一个帖子:总计=总计+1。 (总数= 1)

    2)第二个主题:总计=总计+ ___stop__(上下文切换,主题3的值为3)___val=_3____(总计= 1 + 3 = 4)< / p>

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

    所以一切似乎都很好。

我必须在这里遗漏一些东西。

P.S。 我知道我可以用:

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

但我想学习Parallel.For

2 个答案:

答案 0 :(得分:1)

由于您需要使用锁定以确保正确的结果,我认为Parallel.For不会为您购买任何东西。你不能并行锁定东西;根据定义,锁定是串行完成的。

因此,一个简单的for循环就会表现得更好,也更容易使用。

答案 1 :(得分:1)

是的,还有其他方法可以减少锁定:

  1. 使用overload of Parallel.For() that supports local data。这样,您只需要在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();
    
  5. 关于你的线程安全问题,你假设在“上下文切换”之后(我使用可怕的引号,因为在多核CPU上,没有任何上下文切换来发生此问题),线程将再次读取total的当前值。但事实上,它已经读取了旧值,现在将其保存在寄存器中。因此,步骤3中的结果将变为1 + 2 = 3。