Reputation: 63
I am testing some parallelization solution in c# 4, .NET 4.0.
I have some strange results so I would like to know if I am doing things the right way or not.
Here is a description of my code:
//This will count the number of times we pass in the loop
private static double count_method_5 = 0;
//This will generate a MD5 hash
private static void GenerateMD5Hash(double i)
{
var md5M = MD5.Create();
byte[] data = Encoding.Unicode.GetBytes(Environment.UserName + i.ToString());
byte[] result = md5M.ComputeHash(data);
}
static void Main(string[] args)
{
//Launch method Parallel for method 2
var time9 = watch.ElapsedMilliseconds;
int loop2 = 0;
int limit2 = 300000;
Parallel.For(loop2, limit2, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i =>
{
GenerateMD5Hash(i);
count_method_5++;
loop2++;
});
var time10 = watch.ElapsedMilliseconds;
Console.WriteLine("Parallel For second method (method 5) Elapsed time :" + (time10 - time9) + "ms");
Console.WriteLine("Count method 5 : " + count_method_5);
}
This code is giving me this result:
Count method 5 : 299250
instead of 300000
.
Is this a wrong us of parallelism?
Upvotes: 1
Views: 226
Reputation: 4577
You're probably getting some instances where the number can't be increased because two threads try to edit the variable simultaneously.
You either need to lock
the code that access the variable (so only one thread can only access it at a time), or use Interlocked.Exchange( ref count_method_5, Interlocked.Read(ref count_method_5) + 1)
to perform a threadsafe update of the method.
Actually, having thought about it, it's also possible that one thread is reading the value, then another thread is incrementing it before the first one does - and so you're losing that increment. lock
will solve that problem, but Interlocked.Exchange
by itself won't.
Perhaps the best solution would be to use both? That should get you started, anyway.
i.e:
Object lockObject = new Object();
Parallel.For(loop2, limit2, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i =>
{
GenerateMD5Hash(i);
lock(lockObject){
Interlocked.Exchange( ref count_method_5, Interlocked.Read(ref count_method_5) + 1);}
loop2++;
});
I'll try and show an example of the possible issues:
count_method_5: 1
Thread 1: updating count_method_5
Thread 2: tries to update count_method_5 and fails because Thread 1 is accessing it.
count_method_5: 1
Thread 1: reads count_method_5 as 1
Thread 2: reads count_method_5 as 1
Thread 1: updates count_method_5 to 2 (1 + 1)
Thread 2: updates count_method_5 to 2 (1 + 1)
Therefore, two threads have updated it, but it's only increased by 1.
Update: Instead of the horrendously complicated Interlocked.Exchange( ref count_method_5, Interlocked.Read(ref count_method_5) + 1);
, I have been reminded that you could just use Interlocked.Increment(ref count_method_5);
Upvotes: 0
Reputation: 28355
I think that the solution from @simonalexander2005 a little bit complicated. Why not to use the Interlocked.Increment
method? In this case you can remove the lock for your loop, which will perform better!
Parallel.For(loop2, limit2, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i =>
{
GenerateMD5Hash(i);
Interlocked.Increment(ref count_method_5);
Interlocked.Increment(ref loop2);
});
If you need to add some other value rather than 1
, you can use the Interlocked.Add
method, like this:
Parallel.For(loop2, limit2, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i =>
{
GenerateMD5Hash(i);
Interlocked.Add(ref count_method_5, 5);
Interlocked.Add(ref loop2, 5);
});
You can find other great samples of Interlocked
here. Another option for you is to use a while
loop with Interlocked.CompareExchange
method, but in your case I don't think that this is very important to use.
Upvotes: 1
Reputation: 63
Thank you Simonalexander2005, you are right!
I tryed your solution and it worked! I didn't thought about variable access concurrency!
Perhaps, The ref key-word was missing in the Interlocked.Read call:
Parallel.For(loop2, limit2, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i =>
{
GenerateMD5Hash(i);
lock (lockObject)
{
Interlocked.Exchange(ref count_method_5, Interlocked.Read(ref count_method_5) + 1);
}
loop2++;
});
Thank you very much!
Christophe
Upvotes: 0