5

I have a loop in my code

Parallel.For(0, Cnts.MosqPopulation, i => { DoWork() });

however in the DoWork() function, there are multiple calls to a random number generator which is defined as follows:

public static class Utils
{
    public static readonly Random random = new Random();

}

It's static instance so that it is only seeded once. And I can use it throughout the code.

According to MSDN and other stackoverflow threads, this is not threadsafe. Infact, I have noticed at sometimes my code breaks and the random number generator starts generating all zeros (as per the MSDN documentation).

There are other stackoverflow threads, but are rather old and the implementation is slow. I can't afford to lose time on generating the numbers as the program is a scientific computation and is running hundreds of simulations.

I havn't worked with .net since the 2.0 days and I am not sure how the language has evolved to be able to make a fast, efficient, thread-safe RNG.

Here are the previous threads:

Is C# Random Number Generator thread safe?

Correct way to use Random in multithread application

Fast thread-safe random number generator for C#

Note: because I need a fast implementation, I can not use the RNGCryptoServiceProvider which is rather slow.

Note2: I don't have a minimal working code. I don't even know where to begin as I don't know how thread-safety works or have high level knowledge of c#. So it does seem like I am asking for a complete solution.

Community
  • 1
  • 1
masfenix
  • 7,736
  • 11
  • 45
  • 60
  • Why are the solutions in the posts that you link not suitable for you? Any particular reason? – Dirk Vollmar Jul 22 '16 at 15:29
  • Some of them are really not thread-safe as per their comments. The others are too slow. I was hoping there is a more *modern* version of the previous answers. – masfenix Jul 22 '16 at 15:30
  • 1
    So how about this one: http://stackoverflow.com/a/19271062/40347 – Dirk Vollmar Jul 22 '16 at 15:31
  • @DirkVollmar The comment mentions the possibility of generating the same random number if threads are created too fast. This is very possible in my code because the `DoWork()` is very simple - a few if statements. – masfenix Jul 22 '16 at 15:35
  • Btw, a very good article is this one by Steven Toub: *[Getting random numbers in a thread-safe way](http://blogs.msdn.com/b/pfxteam/archive/2009/02/19/9434171.aspx)* – Dirk Vollmar Jul 22 '16 at 15:36
  • I don't think that the comment is valid, because each thread uses its own seed. – Dirk Vollmar Jul 22 '16 at 15:37
  • 1
    What about this answer? https://stackoverflow.com/questions/2261831/random-numbers-using-c-sharp/2261892#2261892 It links to https://codeblog.jonskeet.uk/2009/11/04/revisiting-randomness/ – dbc Jul 22 '16 at 15:42
  • why not just have each thread create it's own `Random` when it needs it? – Don Cheadle Jul 22 '16 at 15:52

5 Answers5

8

Using the ThreadStatic attribute and a custom getter, you will get a single Random instance per thread. If this is not acceptable, use locks.

public static class Utils
{
    [ThreadStatic]
    private static Random __random;

    public static Random Random => __random??(__random=new Random());
}

The ThreadStatic attribute does not run the initializer on each thread so you are responsible for doing so in your accessor. Also think about your seed initializer, you can use something like

new Random((int) ((1+Thread.CurrentThread.ManagedThreadId) * DateTime.UtcNow.Ticks) )
apk
  • 1,550
  • 1
  • 20
  • 28
Florian Doyon
  • 4,146
  • 1
  • 27
  • 37
2

I would consider something like this:

private static int _tracker = 0;

private static ThreadLocal<Random> _random = new ThreadLocal<Random>(() => {
    var seed = (int)(Environment.TickCount & 0xFFFFFF00 | (byte)(Interlocked.Increment(ref _tracker) % 255));
    var random = new Random(seed);
    return random;
});

I'm not a huge fan of ThreadStatic these days. We have better tools than that using ThreadLocal. Just use _random.Value in your parallel loop and it will give you a new Random per thread.

It combines an atomically incrementing value as well as the default behavior of using Environemnt.TickCount. The incrementing value is there to solve the problem of two Random's getting the same seed. Note that this approach only allows 255 randoms to be created. If you need more, then change the size of the mask.

As you already noted, this isn't usable for secure purposes.

vcsjones
  • 138,677
  • 31
  • 291
  • 286
2

My implementation that combines best approaches from other answers (see design notes in class documentation).

/// <summary>
/// DotNet Random is not ThreadSafe so we need ThreadSafeRandom.
/// See also: https://stackoverflow.com/questions/3049467/is-c-sharp-random-number-generator-thread-safe.
/// Design notes:
/// 1. Uses own Random for each thread (thread local).
/// 2. Seed can be set in ThreadSafeRandom ctor. Note: Be careful - one seed for all threads can lead same values for several threads.
/// 3. ThreadSafeRandom implements Random class for simple usage instead ordinary Random.
/// 4. ThreadSafeRandom can be used by global static instance. Example: `int randomInt = ThreadSafeRandom.Global.Next()`.
/// </summary>
public class ThreadSafeRandom : Random
{
    /// <summary>
    /// Gets global static instance.
    /// </summary>
    public static ThreadSafeRandom Global { get; } = new ThreadSafeRandom();

    // Thread local Random is safe to use on that thread.
    private readonly ThreadLocal<Random> _threadLocalRandom;

    /// <summary>
    /// Initializes a new instance of the <see cref="ThreadSafeRandom"/> class.
    /// </summary>
    /// <param name="seed">Optional seed for <see cref="Random"/>. If not provided then random seed will be used.</param>
    public ThreadSafeRandom(int? seed = null)
    {
        _threadLocalRandom = new ThreadLocal<Random>(() => seed != null ? new Random(seed.Value) : new Random());
    }

    /// <inheritdoc />
    public override int Next() => _threadLocalRandom.Value.Next();

    /// <inheritdoc />
    public override int Next(int maxValue) => _threadLocalRandom.Value.Next(maxValue);

    /// <inheritdoc />
    public override int Next(int minValue, int maxValue) => _threadLocalRandom.Value.Next(minValue, maxValue);

    /// <inheritdoc />
    public override void NextBytes(byte[] buffer) => _threadLocalRandom.Value.NextBytes(buffer);

    /// <inheritdoc />
    public override void NextBytes(Span<byte> buffer) => _threadLocalRandom.Value.NextBytes(buffer);

    /// <inheritdoc />
    public override double NextDouble() => _threadLocalRandom.Value.NextDouble();
}
Alexey.Petriashev
  • 1,634
  • 1
  • 15
  • 19
  • 1
    This is excellent. One tiny suggestion is to put a `
    ` at the end of each line in your opening summary documentation, to make it a bit more readable in the IntelliSense pop-up.
    – wopr_xl Dec 21 '22 at 15:27
0

If you have knowledge of how many threads you are running in parallel this may work:

Random rand = new Random();
var randomNums = Enumerable.Range(0, Cnts.MosqPopulation)
                           .Select(_ => rand.Next()).ToList();
Parallel.For(0, Cnts.MosqPopulation, i => 
{
    Random localRand = new Random(randomNums[i]);
    DoWork();
});

Not sure how indistinguishable the resulting distribution would be from a uniform one though.

ballard26
  • 37
  • 3
-1

You can inherit from Random to build a thread safe random class

public class ThreadsafeRandom : Random
{
    private readonly object _lock = new object();

    public ThreadsafeRandom() : base() { }
    public ThreadsafeRandom( int Seed ) : base( Seed ) { }

    public override int Next()
    {
        lock ( _lock )
        {
            return base.Next();
        }
    }

    public override int Next( int maxValue )
    {
        lock ( _lock )
        {
            return base.Next( maxValue );
        }
    }

    public override int Next( int minValue, int maxValue )
    {
        lock ( _lock )
        {
            return base.Next( minValue, maxValue );
        }
    }

    public override void NextBytes( byte[ ] buffer )
    {
        lock ( _lock )
        {
            base.NextBytes( buffer );
        }
    }

    public override double NextDouble()
    {
        lock ( _lock )
        {
            return base.NextDouble();
        }
    }
}

and use an instance of that class

public static class Utils
{
    public static readonly Random random = new ThreadsafeRandom();

}
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • Locking is the last instrument that need to be use, especially for this cases – ZOXEXIVO Sep 13 '17 at 13:14
  • There's absolutely nothing wrong with this if you for some reason want to use the same random generator across all threads. Now as to why you'd want to do that in a threaded context I'm at a loss, but I don't judge. – tekHedd Jun 08 '23 at 23:12