The Random
class is not thread-safe, meaning concurrent access can lead to duplicate values or other unexpected behavior.
Seeding
In order to understand this, we have to examine how seeding the Random
class works. Let's look at this code:
Random random1 = new Random(42);
Random random2 = new Random(42);
int a = random1.Next(100);
int b = random2.Next(100); // a = b
Console.WriteLine(a);
Console.WriteLine(b); // same output
The code above will lead to the same "random" numbers since both were seeded equaly.
Caution: Instantiating Random
multiple times in a short time frame can lead to duplicate values because the instances may be seeded with the same timestamp. For example it it not guaranteed that this code
Parallel.For(0, 10, i =>
{
Random rnd = new Random();
Console.WriteLine(rnd.Next(100));
});
always returns another value while this code below, using ThreadLocal
, will:
class Program
{
private static ThreadLocal<Random> rnd = new ThreadLocal<Random>(() => new Random());
static void Main()
{
Parallel.For(0, 10, i =>
{
Console.WriteLine(rnd.Value.Next(100));
});
}
}
Alright, that's "the old way". With .NET9 Microsoft introduced Random.Shared
(which is thread-safe) that significantly simplifies this:
Parallel.For(0, 10, i =>
{
Console.WriteLine(Random.Shared.Next(100));
});