我有这个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
答案 0 :(得分:1)
由于您需要使用锁定以确保正确的结果,我认为Parallel.For
不会为您购买任何东西。你不能并行锁定东西;根据定义,锁定是串行完成的。
因此,一个简单的for
循环就会表现得更好,也更容易使用。
答案 1 :(得分:1)
是的,还有其他方法可以减少锁定:
Parallel.For()
that supports local data。这样,您只需要在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。