Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I have a decimal variable that is accessed from multiple threads at the same time. Interlocked class functions do not support decimals at all, so the only approach I'm left with is using lock(){}. It seems to be an overkill.

It there some other way to add value to decimal variable in a thread-safe way?

like image 897
Mikhail Orlov Avatar asked Oct 22 '13 12:10

Mikhail Orlov


People also ask

Is the ++ operator thread-safe?

These operators are not thread-safe. Imagine two threads that increment a variable. If they do the operation serially, the variable ends up correctly incremented twice.

What is interlocked increment?

Increments a specified variable and stores the result, as an atomic operation. Increment(Int64) Increments a specified variable and stores the result, as an atomic operation.

How do you initialize a decimal value?

To initialize a decimal variable, use the suffix m or M. Like as, decimal x = 300.5m;. If the suffix m or M will not use then it is treated as double.

What is interlocked C#?

The methods of this class help protect against errors that can occur when the scheduler switches contexts while a thread is updating a variable that can be accessed by other threads, or when two threads are executing concurrently on separate processors.


1 Answers

You can still use InterLocked, but then you have to convert the decimal to an Int64. With the conversion you have to decide how many decimal places you want to preserve for precision. So for example, you want to preserve 4 decimal places, you could 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;

Be aware that you will lose precision, depending on how many decimal places you would like to preserve. And Decimal.MaxValue is 79,228,162,514,264,337,593,543,950,335 whereas Int64.MaxValue is 9,223,372,036,854,775,807. So very large numbers won't fit. Preserving 4 decimal places, the largest number before the Int64 would overflow is 9,223,372,036,854,775,807 / 10000 = 922,337,203,685,477

I use it this way as numbers here will never go above 1,000,000,000 and I am sure that using Interlocked this way is faster in a Parallel.For loop then using a lock or mutex.

like image 112
Mike de Klerk Avatar answered Sep 29 '22 17:09

Mike de Klerk