DingLi
DingLi

Reputation: 821

Weird behavior of multithread random numbers generator

Please check below code, this code try to compute birthday conflict possibility. To my surprise, if i execute those code with sequence, the result is expected around 0.44; but if try on PLinq, the result is 0.99. Anyone can explain the result?

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


    }

Upvotes: 0

Views: 108

Answers (2)

DingLi
DingLi

Reputation: 821

I find a useful explanation why random does not work under multi-thread, although it was original for Java, still can be benefitical.

Upvotes: -1

Adriano Repetti
Adriano Repetti

Reputation: 67090

You're using a shared (between threads) instance System.Random. It's not thread-safe then you're getting wrong results (well actually it just doesn't work and it'll return 0). From MSDN:

If your app calls Random methods from multiple threads, you must use a synchronization object to ensure that only one thread can access the random number generator at a time. If you don't ensure that the Random object is accessed in a thread-safe way, calls to methods that return random numbers return 0.

Simple (but not so efficient for parallel execution) solution is to use a lock:

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

To improve performance (but you should measure) you may use multiple instances of System.Random, be careful to initialize each one with a different seed.

Upvotes: 3

Related Questions