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.