Reliable Random Number In .NET
using System;
Random r = new Random();
r.Next();
Revisiting randomness
There's nothing special done in the?Next?method to achieve thread safety. However, it's an instance method. If you don't share instances of?Random?across different threads, you don't have to worry about state corruption within an instance. Do not use a single instance of?Random?across different threads without holding an exclusive lock of some sort.
There is another potential problem in using different instances of?Random?that are thread-exclusive, but are seeded identically, and therefore induce the identical sequences of pseudorandom numbers, because they may be created at the same time or within close temporal proximity of each other. One way to alleviate that issue is to use a master?Random?instance (which is locked by a single thread) to generate some random seeds and initialize new?Random?instances for every other thread to use.
The best solution create per-thread instances and using the?ThreadLocal<T> class?or ThreadStaticAttribute .
But what is the difference?
First off these are two different objects altogether, ThreadStatic is actually ThreadStaticAttribute and you mark a static variable with the attribute. Whereas ThreadLocal is a generic class.
[ThreadStatic]
static int _prop;
?
static void Main()
{
???Task t1 = Task.Run(() =>
???{
??????_prop++;
??????Console.WriteLine("t1: " + _prop);
???});
???Task t2 = Task.Run(() =>
???{
??????_prop++;
??????Console.WriteLine("t2: " + _prop);
???});
???Task t3 = Task.Run(() =>
???{
??????_prop++;
??????Console.WriteLine("t3: " + _prop);
???});
?
???Task.WaitAll(t1, t2, t3);
}
The output from this (obviously the ordering may be different) is
t1: 1
t3: 1
t2: 1
One thing to watch out for is that if we initialize the ThreadStatic variable, for example if we write the following
[ThreadStatic]
static int _prop= 10;
you need to be aware that this?is initialized only on the thread it’s declared on, all the threads which use value will get a variable initialised with it’s default value, i.e. 0.
For example, if we change the code very slightly to get
[ThreadStatic]
static int _prop= 10;
?
static void Main()
{
???Task t1 = Task.Run(() =>
???{
??????_prop++;
??????Console.WriteLine("t1: " + _prop);
???});
???Task t2 = Task.Run(() =>
???{
??????_prop++;
??????Console.WriteLine("t2: " + _prop);
???});
???Task t3 = Task.Run(() =>
???{
??????_prop++;
??????Console.WriteLine("t3: " + _prop);
???});
?
???Console.WriteLine("main thread: " + _prop);
????
???Task.WaitAll(t1, t2, t3);
}
The output will look something like
main thread: 10
t2: 1
t1: 1
t3: 1
Finally as, by definition each variable is per thread, operations upon the instance of the variable are thread safe.
ThreadLocal
Like the ThreadStatic attribute, the ThreadLocal class allows us to declare a variable which is not shared between threads, one of the extra capabilities of this class is that we can initialize each instance of the variable as the class the supplied factory method to create and/or initialize the value to be returned. Also, unlike ThreadStatic which only works on static fields, ThreadLocal can be applied to static or instance variables.
Let’s look at the code
static void Main()
{
???ThreadLocal<int> _prop= new ThreadLocal<int>(() =>
???{
??????return 20;
???});
?
???Task t1 = Task.Run(() =>
???{
??????_prop.Value++;
??????Console.WriteLine("t1: " + _prop.Value);
???});
???Task t2 = Task.Run(() =>
???{
??????_prop.Value++;
??????Console.WriteLine("t2: " + _prop.Value);
???});
???Task t3 = Task.Run(() =>
???{
??????_prop.Value++;
??????Console.WriteLine("t3: " + _prop.Value);
???});
?
???Task.WaitAll(t1, t2, t3);
???local.Dispose();
}
The output order may be different, but the output will read something like
t2: 21
t3: 21
t1: 21
As you can see, each thread altered their own instance of the thread local variable and more over we were able to set the default value to?20.
The ThreadLocal class implements IDisposable, so we should Dispose of it when we’ve finished with it.
So to create a Trustworthy Random Number you do not need to know the object information in the first place
? ? public class RandomGen
? ? {
? ? ? ? private static RNGCryptoServiceProvider _global = new RNGCryptoServiceProvider();
? ? ? ? [ThreadStatic]
? ? ? ? private static Random _local;
? ? ? ? public static int Next(int minValue, int maxValue)
? ? ? ? {
? ? ? ? ? ? Random inst = _local;
? ? ? ? ? ? if (inst is null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? byte[] buffer = new byte[4];
? ? ? ? ? ? ? ? _global.GetBytes(buffer);
? ? ? ? ? ? ? ? _local = inst = new Random(BitConverter.ToInt32(buffer, 0));
? ? ? ? ? ? }
? ? ? ? ? ? return inst.Next();
? ? ? ? }
? ? }
Create a static field?from Random and place the ThreadStatic attribute on top of it and buffer object create empty array from byte then RNGCryptoServiceProvider fills buffer with cryptographically strong sequence of random values finally in first step Randoms send converted buffer to constructor for seed .
Software Engineer at Meta
3 å¹´You dont need to worry about identical seeding while creating new instances of Random in close temporal proximity, this issue is fixed in the .Net Core and uses the logic similar to your RandomGen class. So you can just use ThreadLocal to create one instance of Random per thread: ThreadLocal<Random> = new ThreadLocal<Random>(() => new Random());
Senior Software Engineer @ Microsoft
3 å¹´Thanks. I'd like to add that for async code we should go with AsyncLocal to keep the data in the same async flow. The reason is async flow might be executed by different threads along its lifetime.