我正在测试c#4,.NET 4.0中的一些并行化解决方案。
我有一些奇怪的结果,所以我想知道我是否以正确的方式做事。
以下是我的代码说明:
//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);
}
这段代码给了我这个结果:
计数方法5:299250
而不是300000
。
这对并行性是错误的吗?
答案 0 :(得分:1)
我认为来自@ simonalexander2005的解决方案有点复杂。为什么不使用Interlocked.Increment
方法?在这种情况下,您可以删除循环的锁定,这将表现更好!
Parallel.For(loop2, limit2, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i =>
{
GenerateMD5Hash(i);
Interlocked.Increment(ref count_method_5);
Interlocked.Increment(ref loop2);
});
如果您需要添加其他值而非1
,则可以使用Interlocked.Add
方法,如下所示:
Parallel.For(loop2, limit2, new ParallelOptions { MaxDegreeOfParallelism = 8 }, i =>
{
GenerateMD5Hash(i);
Interlocked.Add(ref count_method_5, 5);
Interlocked.Add(ref loop2, 5);
});
您可以找到Interlocked
here的其他精彩示例。另一个选择是使用while
循环和Interlocked.CompareExchange
方法,但在您的情况下,我认为这不是非常重要的使用。
答案 1 :(得分:0)
你可能会遇到一些无法增加数量的实例,因为两个线程试图同时编辑变量。
您需要lock
访问变量的代码(因此一次只能访问一个线程),或使用Interlocked.Exchange( ref count_method_5, Interlocked.Read(ref count_method_5) + 1)
执行方法的线程安全更新。
实际上,考虑过它,一个线程也可能正在读取值,然后另一个线程在第一个线程之前递增它 - 所以你就失去了这个增量。 lock
将解决该问题,但Interlocked.Exchange
本身不会。
也许最好的解决方案是同时使用两者?无论如何,这应该让你开始。
即:
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++;
});
我会尝试展示可能出现问题的示例:
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)
因此,两个线程已更新它,但它只增加了1。
更新:我提醒您可以使用Interlocked.Exchange( ref count_method_5, Interlocked.Read(ref count_method_5) + 1);
Interlocked.Increment(ref count_method_5);
。
答案 2 :(得分:0)
谢谢Simonalexander2005,你是对的!
我尝试了你的解决方案,但它确实有效!我没有考虑变量访问并发性!
或许,Interlocked.Read调用中缺少ref关键字:
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++;
});
非常感谢!
克里斯托弗