多线程随机数发生器的奇怪行为

时间:2014-07-10 08:57:58

标签: c# random plinq

请检查下面的代码,此代码尝试计算生日冲突的可能性。令我惊讶的是,如果我用序列执行这些代码,结果预计大约为0.44;但如果尝试使用PLinq,结果为0.99。 任何人都可以解释结果?

    public static void BirthdayConflict(int num = 5, int people = 300) {

        int N = 100000;

        int act = 0;
        Random r = new Random();

        Action<int> action = (a) =>     { 
            List<int> p = new List<int>();
            for (int i = 0; i < people; i++)
            {
                p.Add(r.Next(364) + 1);
            }

            p.Sort();

            bool b = false;

            for (int i = 0; i < 300; i++)
            {

                if (i + num -1 >= people) break;

                if (p[i] == p[i + num -1])
                    b = true;
            }

            if (b)
                Interlocked.Increment(ref act);
               // act++;

        };


        // Result is around 0.99 - which is not OK
       // Parallel.For( 0, N, action);

        //Result is around 0.44 - which is OK
        for (int i = 0; i < N; i++)
        {
            action(0);
        }

        Console.WriteLine(act / 100000.0);

        Console.ReadLine();


    }

2 个答案:

答案 0 :(得分:3)

您正在使用共享(线程之间)实例System.Random它不是线程安全的然后你得到错误的结果(实际上它只是没有工作而且它会返回0)。来自MSDN:

  

如果您的应用从多个线程调用随机方法,则必须使用同步对象以确保一次只有一个线程可以访问随机数生成器。如果您不确保以线程安全的方式访问Random对象,则对返回随机数的方法的调用将返回0.

简单(但并行执行效率不高)解决方案是使用锁:

lock (r)
{
    for (int i = 0; i < people; i++)
    {
        p.Add(r.Next(364) + 1);
    }
}

为了提高性能(但您应该测量),您可以使用System.Random的多个实例,请小心使用不同的种子初始化每个实例。

答案 1 :(得分:-1)

我找到了一个有用的解释why random does not work under multi-thread,虽然它是Java的原创,但仍然可以是有益的。