Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementing a Random provider in .Net 3.5

Here is the culmination of a Skeet posting for a random provider:

public static class RandomProvider
{    
    private static int seed = Environment.TickCount;

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() =>
        new Random(Interlocked.Increment(ref seed))
    );

    public static Random GetThreadRandom()
    {
        return randomWrapper.Value;
    }
}

I would like to use the same concept in a .NET 3.5 project, so ThreadLocal is not an option.

How would you modify the code to have a thread safe random provider without the help of ThreadLocal?

UPDATE

Ok, am going with Simon's [ThreadStatic] for now since I understand it the best. Lots of good info here to review and rethink as time allows. Thanks all!

public static class RandomProvider
{
    private static int _seed = Environment.TickCount;

    [ThreadStatic]
    private static Random _random;

    /// <summary>
    /// Gets the thread safe random.
    /// </summary>
    /// <returns></returns>
    public static Random GetThreadRandom() { return _random ?? (_random = new Random(Interlocked.Increment(ref _seed))); }
}
like image 360
Berryl Avatar asked Mar 16 '12 17:03

Berryl


3 Answers

How would you modify the code to have a thread safe random provider without the help of ThreadLocal?

Jon answers your question in the article you linked to:

use one instance, but also use a lock which every caller has to remember to acquire while they're using the random number generator. That can be simplified by using a wrapper which does the locking for you, but in a heavily multithreaded system you'll still potentially waste a lot of time waiting for locks.

So just lock it every time in the wrapper, and unlock it when you're done.

If that is cheap enough, great, it's cheap enough.

If that is not cheap enough then you have two choices. First, make it cheaper. Second, write a threadsafe implementation of a pseudo-random-number generator that can be used without locking.

There are a number of ways to make it cheaper. For example, you could trade space for time; you could generate an array of a hundred thousand random numbers when the program starts up, and then write a lock-free algorithm that proffers up previously-computed random values from the array. When you run out of values, generate another hundred thousand values in an array, and swap the new array for the old one.

That has the downside that its memory consumption is about a hundred thousand times larger than it could be, and that every hundred thousand numbers suddenly it gets really slow, and then speeds up again. If that's unacceptable then come up with a strategy that is acceptable. You're the one who knows what is acceptable performance and what isn't.

Or, like I said, write your own if you don't like the one that is provided for you. Write a threadsafe implementation of Random with acceptable performance and use it from multiple threads.

I saw what Jon said about locking in the wrapper but not sure how the code would look!

Something like:

sealed class SafeRandom
{
    private Random random = new Random();
    public int Next()
    {
        lock(random)
        {
            return random.Next();
        }
    }
}

Now every time you call Next, you take out a lock. (Always lock on a private object; that way you know that your code is the only code locking it!) If two threads call Next "at the same time" then the "loser" blocks until the "winner" leaves the Next method.

If you wanted, you could even make the SafeRandom object a static class:

static class SafeRandom
{
    private static Random random = new Random();
    public static int Next()
    {
        lock(random)
        {
            return random.Next();
        }
    }
}

and now you can call SafeRandom.Next() from any thread.

like image 172
Eric Lippert Avatar answered Oct 17 '22 14:10

Eric Lippert


You can use the ThreadStaticAttribute

[ThreadStatic]
private static Random _random;

private static Random Random
{
    get
    {
        int seed = Environment.TickCount;

        return _random ?? (_random = new Random(Interlocked.Increment(ref seed)))
    }
}
like image 22
Terkel Avatar answered Oct 17 '22 13:10

Terkel


I just read the link to "C# in Depth" and, although I agree to the fact that Random being not threadsafe is a pain, I would actually use a different approach to getting rid of the problem, namely for performance reason.

Instantiating a Random engine is quite a heavy action, so I'd rather keep only one instance and make it thread safe through the use of locks :

public static class RandomProvider 
{     
    private static Random randomEngine = new Random(Environment.TickCount);

    private static object randomLock = new object();

    public static int GetRandomValue() 
    { 
        lock(randomLock)
        {
            return randomEngine.Next();
        }
    } 
} 

HTH

like image 1
JP Gouigoux Avatar answered Oct 17 '22 14:10

JP Gouigoux