使用 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()
重载。这样,您只需要在localFinally
委托中进行同步(但您不应该忘记它)。 - 使用
Interlocked.Add()
.这在您的情况下不起作用,因为重载仅适用于int
和long
,不适用于decimal
。 - 不要使用并行处理。通过像这样非常简单的操作,并行处理的开销很可能超过速度的提高。
-
使用 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
循环将同样具有性能,并且更易于使用。