I have this DataTable:
DataTable dt = GetDatatTable();
One of its column is Amount (decimal)
I want to summarize it as fast as I can using 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"]) });
But I really dont want to lock around many times.
Question #1
Is there any other alternative which reduce the extensive locks ?
Question #2
I don’t understand why should I need to protect the total accumulator
-
Does the protection is for the
+=or for multi thread updating thetotal?I mean look at the following flow , a
Volatilefield can solve it easily.let’s say
total=0
and the DataTable items are1,2,31) first thread : total= total+1. ( total=1)
2) second thread : total = total+
___stop__( context switch , thread 3 comes in with value 3)___val=_3____( total =1+3=4)3) context switch back to thread 2 total=4+2 = 6.
so everything seems to be fine .
I must be missing something here.
P.s.
I know I can do it with :
ParallelEnumerable.Range (1, dt.Rows.Count+1).Sum (i => Decimal.Parse(dt.Rows[i-1]["Amount"]) )
But I want to learn to do it with Parallel.For
Yes, there are alternatives to reduce the locks:
Parallel.For()that supports local data. This way, you need synchronization only in thelocalFinallydelegate (but you shouldn’t forget it there).Interlocked.Add(). This won’t work in your case, because there are overloads only forintandlong, not fordecimal.Use PLINQ:
Regarding your thread-safety question, you’re assuming that after the “context switch” (I use scary quotes, because on multicore CPUs, there doesn’t have to be any context switch for this issue to occur), the thread will read the current value of
totalagain. But in fact, it already read the old value, which is now saved in a register. So, the result in step 3 will become 1 + 2 = 3.