C# Random Number Generator getting stuck in a cycl

2019-06-22 07:09发布

问题:

I am using .NET to create an artificial life program and I am using C#'s pseudo random class defined in a Singleton. The idea is that if I use the same random number generator throughout the application, I could merely save the seed and then reload from the seed to recompute a certain interesting run.

public sealed class RandomNumberGenerator : Random
{
    private static readonly RandomNumberGenerator instance = new RandomNumberGenerator();

    RandomNumberGenerator()
    {

    }

    public static RandomNumberGenerator Instance
    {
        get
        {
            return instance;
        }
    }
}

I also wanted a method that could give me two different random numbers.

public static Tuple<int, int> TwoDifferentRandomNumbers(this Random rnd, int minValue, int maxValue)
    {
        if (minValue >= maxValue)
            throw new ArgumentOutOfRangeException("maxValue", "maxValue must be greater than minValue");
        if (minValue + 1 == maxValue)
            return Tuple.Create<int, int>(minValue, maxValue);

        int rnd1 = rnd.Next(minValue, maxValue);
        int rnd2 = rnd.Next(minValue, maxValue);
        while (rnd1 == rnd2)
        {                
            rnd2 = rnd.Next(minValue, maxValue);
        }
        return Tuple.Create<int, int>(rnd1, rnd2);            
    }

The problem is that sometimes rnd.Next(minValue,maxValuealways returns minValue. If I breakpoint at this point and try creating a double and setting it to rnd.NextDouble(), it returns 0.0. Anyone know why this is happening?

I know that it is a pseudo random number generator, but frankly, I hadn't expected it to lock at 0. The random number generator is being accessed from multiple threads... could this be the source of the problem?

EDIT : Thanks, the problem ended up being thread safety.

This is the new version of the class.

 public sealed class RandomNumberGenerator : Random
{
    private static Random _global = new Random();
    [ThreadStatic]
    private static Random _localInstance;

    RandomNumberGenerator()
    {

    }

    public static Random Instance
    {
        get
        {
            Random inst = _localInstance;
            if (inst == null)
            {
                int seed;
                lock (_global) seed = _global.Next();
                _localInstance = inst = new Random(seed);
            }
            return _localInstance;
        }
    }
}

回答1:

If you use only One RNG for multiple threads even if you save the seed, you won't be able to generate the same numbers the next time you launch your application because you won't be sure that the calls to the RNG from the different threads will be in the same order.

If you have a fixed/known number of thread make a RNG per Thread and save each seed.

Forget what I just said if you're 100% sure that each thread will call the RNG with exactly the same order as the last time if you use the same seed.



回答2:

The Random class is not thread-safe.

You should make your static instance [ThreadStatic], or protect it with a lock.



回答3:

The idea is that if I use the same random number generator throughout the application, I could merely save the seed and then reload from the seed to recompute a certain interesting run.

You don't actually need a singleton instance of the RNG for this. If you initialize two separate instances of Random to the same seed, they will produce exactly the same sequence.

My advice is, save the seed, but get rid of the singleton.



回答4:

I don't even have to look up the Random class th know "all instance methods of this class or not thread-safe". That goes for all .NET classes, with a very few exceptions.

So yes, it is the multi-threading. But you haven't mentioned verifying that MaxValue > MinValue either.