0

私はこのDataTableを持っています:

DataTable dt = GetDatatTable();

その列の1つはAmount(10進数)です

を使用してできるだけ早く要約したいと思い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) 2番目のスレッド:total = total + ___stop__ (コンテキストスイッチ、スレッド3には値3が含まれます) ___val=_3____(total = 1 + 3 = 4)

    3)コンテキストスイッチをスレッド2に戻しますtotal = 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

はい、ロックを減らすための代替手段があります。

  1. ローカルデータをサポートするのオーバーロードをParallel.For()使用します。このように、デリゲートでのみ同期が必要ですlocalFinally(ただし、そこで忘れてはなりません)。
  2. を使用しInterlocked.Add()ます。intの場合ではなく、との場合にのみオーバーロードがあるため、これはあなたのケースでは機能し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 に答える
1

Parallel.For正しい結果を得るにはロックを使用する必要があるため、何も購入する必要はないと思います。何かを並行してロックすることはできません。定義上、ロックは連続して行われます。

そのため、単純なforループでも同様にパフォーマンスが向上し、操作がはるかに簡単になります。

于 2013-01-29T08:44:46.513 に答える