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 -
- ThreadStatic Random instance, with a global random instance to generate seeds
- 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:
- 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. - 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.
- 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, useSystem.Random
, but if you want to generate a random secret string, useRandomNumberGenerator
.
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/