2

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();
        }
    }
Ben
  • 793
  • 2
  • 13
  • 29
  • Is your question related to this? [Is C# Random Number Generator thread safe?](https://stackoverflow.com/questions/3049467/is-c-sharp-random-number-generator-thread-safe) – Theodor Zoulias Feb 23 '22 at 15:12
  • Yes it is related, this is where I found the first article iv'e shared in my question. But as this answer is 10 years-old, and since then dotnet has revisted System.Random Iv'e tried looking for a more up to date solution. – Ben Feb 23 '22 at 19:36

2 Answers2

10

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://learn.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/

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • 1
    Thanks for the info! So if a cryptographically strong randomness isn't a requirement and Im still not using .NET 6, I simply need a static instance of ThreadLocal Random and Im good to go? no additional code is required? – Ben Feb 24 '22 at 08:38
  • @Ben - That is correct. – Enigmativity Feb 24 '22 at 11:12
1
  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.
tia
  • 9,518
  • 1
  • 30
  • 44
  • Ty for the answer. Updated my initial question with code samples, can you take a look? ty – Ben Feb 23 '22 at 19:45
  • It is ok for me as it should work correctly, but you might also use a static `ThreadLocal`, which will initiate Random instance for each thread for you so you don't need to do lazy instantiation by yourself like that. – tia Feb 24 '22 at 06:06