0

我有这个数据表:

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)第一个线程:total=total+1。(总计=1)

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

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

    所以一切似乎都很好。

我一定在这里遗漏了一些东西。

PS 我知道我可以做到:

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

但我想学着做Parallel.For

4

2 回答 2

1

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

因此,一个简单的for循环将同样高效,并且更易于使用。

于 2013-01-29T08:44:46.513 回答
1

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

  1. 使用支持本地数据的重载Parallel.For()。这样,您只需要在localFinally委托中进行同步(但您不应该在那里忘记它)。
  2. 使用Interlocked.Add(). 这在您的情况下不起作用,因为只有intand有重载,而long不是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。

于 2013-01-29T13:18:06.560 回答