Is C# Random Number Generator Thread Safe

Is C# Random Number Generator thread safe?

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.

Jon Skeet has a couple nice posts on this subject:

StaticRandom

Revisiting randomness

As noted by some commentators, 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.

How to implement a thread-safe random

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/

Thread safe high performance random generator

You can do it without locking and still be threadsafe. Assuming the calculation is very fast (it is) and there is slower code executing around it, it's likely faster to simply recalculate if another thread changes it in between starting the calculation and finishing it. You can do that with an Interlocked.CompareExchange spin loop. The only difficulty is that there is no ulong version of that so we have to use an unsafe method to get the equivalent.

private static unsafe ulong InterlockedCompareExchange(ref ulong location,
ulong value, ulong comparand)
{
fixed (ulong* ptr = &location)
{
return (ulong)Interlocked.CompareExchange(ref *(long*)ptr, (long)value, (long)comparand);
}
}

public ulong GetULong()
{
unchecked
{
ulong prev = seed;

ulong t = prev;
t ^= t >> 12;
t ^= t << 25;
t ^= t >> 27;

while (InterlockedCompareExchange(ref seed, t, prev) != prev)
{
prev = seed;
t = prev;
t ^= t >> 12;
t ^= t << 25;
t ^= t >> 27;
}

return t * 0x2545F4914F6CDD1D;
}
}

Getting random numbers in a thread-safe way

Note: since writing this answer, I've become aware of issues creating multiple Random instances, even though it sounds like it should work. I've generally found that a better alternative is to have a single Random instance and just lock on that. Although that is a single potential bottleneck, in most applications it won't cause problems.


I suspect it's just to avoid the cost of reading the thread-static variable multiple times. It's relatively inefficient to do so, compared with reading a local variable.

Your suggestion would work, but it would be slightly less efficient, that's all. In other cases, there would be the possibility of the value changing between fetches - but of course that's not a problem in this case, as it's a thread-local variable.

With .NET 4 and higher, this would be simpler using ThreadLocal<T>:

public static class RandomGen2 
{
private static Random _global = new Random();
private static ThreadLocal<Random> _local = new ThreadLocal<Random>(() =>
{
int seed;
lock (_global) seed = _global.Next();
return new Random(seed);
});


public static int Next()
{
return _local.Value.Next();
}
}

Is Random.Next not Thread-Safe and returns 0 in .netcore

If you run the following console application (RELEASE build), you should see occasional sequences of zeros in the output.

It doesn't happen for every random value, but when it happens, you get quite a few zeroes in a row:

static class Program
{
static void Main()
{
var rng = new Random(12345);

Parallel.Invoke(
() => test(rng),
() => test(rng),
() => test(rng),
() => test(rng),
() => test(rng),
() => test(rng),
() => test(rng),
() => test(rng));
}

static void test(Random rng)
{
int[] numbers = new int[10000];

for (int i = 0; i < numbers.Length; ++i)
numbers[i] = rng.Next();

for (int i = 0; i < numbers.Length; ++i)
Console.WriteLine(numbers[i]);
}
}

Note that it doesn't happen (or happens a lot less) for a DEBUG build; this is the nature of threading errors.

It doesn't even happen on every run for a RELEASE build, but it does happen sometimes (on my PC).

Fastest, thread safe way to create crypto-random numbers in c#?

The documentation for RNGCryptoServiceProvider states:

This type is thread safe.

Your code doesn't demonstrate that RNGCryptoServiceProvider is not thread-safe, since you use the same array in multiple threads. That array reuse is not thread-safe even if RNGCryptoServiceProvider is.

Regarding performance I want to note that creating a new instance of RNGCryptoServiceProvider is very cheap. The expensive part is the per-call overhead of GetBytes.

So if you have performance trouble, the first thing I'd try is asking for more data in one go and splitting it yourself. If that's still not enough, use a stream cipher seeded by the system CSPRNG.

Correct way to use Random in multithread application

I use something like this:

public static class StaticRandom
{
static int seed = Environment.TickCount;

static readonly ThreadLocal<Random> random =
new ThreadLocal<Random>(() => new Random(Interlocked.Increment(ref seed)));

public static int Rand()
{
return random.Value.Next();
}
}

Fast thread-safe random number generator for C#

Here is my take on it (requires .net 4.0):

public static class RandomGenerator
{
private static object locker = new object();
private static Random seedGenerator = new Random(Environment.TickCount);

public static double GetRandomNumber()
{
int seed;

lock (locker)
{
seed = seedGenerator.Next(int.MinValue, int.MaxValue);
}

var random = new Random(seed);

return random.NextDouble();
}
}

and a test to check that for 1000 iterations each value is unique:

[TestFixture]
public class RandomGeneratorTests
{
[Test]
public void GetRandomNumber()
{
var collection = new BlockingCollection<double>();

Parallel.ForEach(Enumerable.Range(0, 1000), i =>
{
var random = RandomGenerator.GetRandomNumber();
collection.Add(random);
});

CollectionAssert.AllItemsAreUnique(collection);
}
}

I don't guarantee that it will never return a duplicate value, but I've run the test with 10000 iterations and it passed the test.



Related Topics



Leave a reply



Submit