1

我正在 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.

这是并行性的错误吗?

4

3 回答 3

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 您可以在这里找到其他很棒的示例。您的另一个选择是使用while带有方法的循环Interlocked.CompareExchange,但在您的情况下,我认为这不是很重要。

于 2016-05-13T12:33:43.043 回答
0

您可能会遇到一些无法增加数字的情况,因为两个线程试图同时编辑变量。

您要么需要lock访问变量的代码(因此一次只能访问一个线程),要么用于Interlocked.Exchange( ref count_method_5, Interlocked.Read(ref count_method_5) + 1)执行方法的线程安全更新。

实际上,考虑到这一点,也有可能一个线程正在读取该值,然后另一个线程在第一个线程之前递增它 - 所以你失去了那个增量。lock会解决这个问题,但Interlocked.Exchange它本身不会。

也许最好的解决方案是同时使用两者?无论如何,这应该让你开始。

IE:

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

我将尝试展示可能出现的问题的示例:

  1. 锁定:

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.

  1. 损失增量:

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

于 2016-05-10T10:41:11.233 回答
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++;
});

非常感谢你!

克里斯托夫

于 2016-05-10T11:16:44.203 回答