How to increase (add value) to a decimal stream in a thread-safe way?

I have a decimal variable that is accessed from multiple threads at the same time. Interlocked functions of the class does not support decimal numbers in general, so the only approach with which I remain, - is to use lock(){} . It seems to be too much.

Is there any other way to add a value to a decimal variable in a thread safe way?

+6
source share
4 answers

Using a lock is not redundant. Required.

Structural types, such as System.Decimal, are never atomic, but also do not fit the size of their own processor. This is why Interlocked has no overload for it.

+12
source

No. The internal representation of decimal too complicated to make changes using atomic instructions at the CPU level (this is what Interlocked does most of the time, and this is what interests you).

When the CPU cannot handle the atoms manually, the number of manual locks is the only option. You can choose a synchronization primitive (for example, lock versus a mutex), but what is it.

+3
source

You can still use InterLocked , but then you need to convert the decimal mark to Int64 . When converting, you need to decide how many decimal places you want to keep for accuracy. So, for example, you want to save 4 decimal places, you can do something like this:

  //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; 

Remember that you will lose accuracy, depending on how many decimal places you would like to keep. And Decimal.MaxValue is 79,228,162,514,264,337,593,543,950,335 , while Int64.MaxValue is 9,223,372,036,854,775,807 . So very large numbers do not fit. Keeping 4 decimal places, the largest number before the Int64 overflow will be 9,223,372,036,854,775,807 / 10000 = 922,337,203,685,477

I use it this way since the numbers here will never exceed 1,000,000,000, and I am sure that using InterLocked this way is faster in the Parallel.For loop, using lock or mutex.

0
source

If you don't mind keeping the total as wrapped by a decimal object, you can use this approach:

 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); } 

Although this approach does not use the lock, he suffers decimal to an object that bears its consequences for performance. Using locks may be cheaper depending on the situation.

0
source

Source: https://habr.com/ru/post/956479/


All Articles