2013-10-22 7 views
6

複数のスレッドから同時にアクセスされる変数がdecimalです。 Interlockedクラス関数は小数点をまったくサポートしていないので、残っている唯一のアプローチはlock(){}です。それは過労だと思われる。スレッドセーフな方法で10進数をインクリメント(値を加算)する方法はありますか?

decimal変数に値をスレッドセーフな方法で追加する方法はありますか?

答えて

12

ロックを使用することは過度の攻撃ではありません。 が必要ですです。

System.Decimalのような構造型は決してアトミックではなく、ネイティブのCPUワードサイズにも適合しません。 Interlockedにはそのためのオーバーロードもありません。

3

いいえdecimalの内部表現は、CPUレベルでアトミック命令で変更するには複雑すぎます(これはほとんどの場合Interlockedであり、興味のあるものです)。

CPUが原子的に何らかの量を処理できない場合、手動ロックが唯一のオプションです。同期プリミティブ(たとえばlockとmutex)を選択できますが、それだけです。

0

InterLockedを使用できますが、小数点をInt64に変換する必要があります。コンバージョンでは、小数点以下の桁数を精度のために保持する必要があります。

 //Declare up front accessible from all threads 
     Int64 totalAmount = 0; 

     //Inside the thread you do this 
     var amount = (Int64)(decimalAmount * 10000); //10.000 is to preserve 4 decimal places 
     Interlocked.Add(ref totalAmount, amount); 

     //After all threads have finished, go back to decimal type. 
     var totalDecimalAmount = totalAmount/10000; 

はあなたが保存したいと思いますどのように多くの小数点以下の桁に応じて、精度を失うことに注意してください:たとえば、あなたは小数点以下4桁を維持したいので、あなたはこのような何かを行うことができます。 Decimal.MaxValue79,228,162,514,264,337,593,543,950,335であり、Int64.MaxValue9,223,372,036,854,775,807である。だから非常に大きな数字は合わないでしょう。 Int64型がオーバーフローする前に、最大の数を小数点以下4桁を維持することが、ここでの数字は10億の上に行くことは決してありませんし、私はInterlockedを使用すると、この方法は、その後、Parallel.Forループに高速であることを確信しているように私はそれをこのように使用9,223,372,036,854,775,807/10000 = 922,337,203,685,477

ですlockまたはmutexを使用します。あなたがオブジェクトでラップdecimalとして合計を維持する気にしない場合は

0

は、あなたがこのアプローチを使用できます。このアプローチは、ロックを使用しませんが

private static object myTotal = 0M; 
static void InterlockedAddTotal(decimal val) { 
    object next; 
    object current; 
    do { 
     current = myTotal; 
     next = val + (decimal)current; 
    } while (Interlocked.CompareExchange(ref myTotal, next, current) != current); 
} 

を、それが運ぶオブジェクトにdecimalをラップそれ自体のパフォーマンスに影響します。状況によっては、ロックを使用するほうが安いかもしれません。

関連する問題