Search code examples
c#.netmultithreadingrandomthread-safety

Getting random numbers in a thread-safe way


Here is a nice article describing thread safety of random numbers:Getting random numbers in a thread-safe way

But I'm stuck with the "RandomGen2" example:

public static class RandomGen2 
{ 
    private static Random _global = new Random(); 
    [ThreadStatic] 
    private static Random _local;

    public static int Next() 
    { 
       Random inst = _local; 
       if (inst == null) 
       { 
           int seed; 
           lock (_global) seed = _global.Next(); 
           _local = inst = new Random(seed); 
       } 
       return inst.Next(); 
   } 

}

Why is the thread static field copied to local variable: Random inst = _local; ? Why not to simply use

if (_local == null) .... return _local.Next()


Solution

  • 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();
        } 
    }