how to make a c# thread-safe random number generator

前端 未结 4 684
粉色の甜心
粉色の甜心 2021-01-14 04:27

I have a loop in my code

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

however in the DoWork() function, there a

相关标签:
4条回答
  • 2021-01-14 04:44

    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.

    0 讨论(0)
  • 2021-01-14 04:51

    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) )
    
    0 讨论(0)
  • 2021-01-14 04:51

    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.

    0 讨论(0)
  • 2021-01-14 05:00

    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();
    
    }
    
    0 讨论(0)
提交回复
热议问题