Home > Net >  How to implement a thread-safe random
How to implement a thread-safe random

Time:02-24

Trying to find and understand the best approach to implement a thread-safe number generator in .NET Core 2.x or higher

First Iv'e found this - https://web.archive.org/web/20160326010328/http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx

After reading through it seems to me like there are couple of "good" ways -

  1. ThreadStatic Random instance, with a global random instance to generate seeds
  2. ThreadStatic Random instance, with a global RNGCryptoServiceProvider to generate seeds

Where basically you choose the latter if a strong cryptographically random is a requirement.

After some additional research I found out that since .NET Core 2.x System.Random class was revised, therefore the default seed generation which is no longer primary dependent on the system timer. (https://blogs.siliconorchid.com/post/coding-inspiration/randomness-in-dotnet)

Question - How does this affect the implementation of a thread-safe random class?

Refrencing the first link Iv'e shared code solution -

public static class RandomGen2
{
    private static Random _global = new Random();
    [ThreadStatic]
    private static Random _local;

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

Since dotnet core 2.x adjustments is a global locked seed generator even required? or a basic ThreadStatic random instance is all thats needed? such as -

    public static class ThreadSafeRandom
    {
        [ThreadStatic]
        private static Random _local;

        public static int Next()
        {
            Random inst = _local;
            if (inst == null)
            {
                _local = inst = new Random();
            }
            return inst.Next();
        }
    }

CodePudding user response:

  1. The algorithm the System.Random class use when instantiated with default constructor has changed to the new one, but the class is still not thread-safe, so old technique like using [ThreadStatic] is still needed.
  2. In .NET Core, the default seed value is produced by the thread-static, pseudo-random number generator. That means you don't need to implement global seed generator anymore.
  3. Use RandomNumberGenerator when you really want to make sure your random number is secure. For example, if you just want to display random daily messages, use System.Random, but if you want to generate a random secret string, use RandomNumberGenerator.

CodePudding user response:

From .NET 6 you can use Random.Shared to get a thread-safe instance of Random.

The documents say this:

Provides a thread-safe Random instance that may be used concurrently from any thread.

https://docs.microsoft.com/en-us/dotnet/api/system.random.shared?view=net-6.0

There's no need to get fancy anymore.

To get a random integer you just need to do:

int number = Random.Shared.Next();

If you want cryptographically strong randomness, then Eric Lippert's BetterRandom is the way to go:

public static class BetterRandom
{
    private static readonly ThreadLocal<System.Security.Cryptography.RandomNumberGenerator> crng = new ThreadLocal<System.Security.Cryptography.RandomNumberGenerator>(System.Security.Cryptography.RandomNumberGenerator.Create);
    private static readonly ThreadLocal<byte[]> bytes = new ThreadLocal<byte[]>(() => new byte[sizeof(int)]);
    public static int NextInt()
    {
        crng.Value.GetBytes(bytes.Value);
        return BitConverter.ToInt32(bytes.Value, 0) & int.MaxValue;
    }
    public static double NextDouble()
    {
        while (true)
        {
            long x = NextInt() & 0x001FFFFF;
            x <<= 31;
            x |= (long)NextInt();
            double n = x;
            const double d = 1L << 52;
            double q = n / d;
            if (q != 1.0)
                return q;
        }
    }
}

Start here to read more: https://ericlippert.com/2019/01/31/fixing-random-part-1/

  • Related