Why do two tasks created after each other generate the same random value?
Parameterless Random
class constructor uses time-related way to determine initial seed for random numbers generation algorithm.
public Random()
: this(Environment.TickCount) {
}
That's why when you create instances at the same time they will generate the same results.
I don't understand how this is related to "Random String Generator Returning Same String".
It’s not directly related, although the root cause is the same. A better duplicate would be this question: Why do I keep getting two of same random values in this code?
It contains an explanation of what new Random
does – courtesy to the documentation:
The default seed value is derived from the system clock and has finite resolution. As a result, different Random objects that are created in close succession by a call to the default constructor will have identical default seed values and, therefore, will produce identical sets of random numbers.
In other words: if you create Random
objects in quick succession, they will produce the same random number sequence.
He was creating the random instance in the method. I am calling it in completely different Tasks so they should be independent of each other.
It is irrelevant whether these objects are created in different threads (or Task
s) – they only depend on the system time when they are created, on nothing else. They are in fact independent from each other, like you said. But they both depend on the same seed value, which is the system time at creation.
The correct way of solving this is usually to have only one instance of the Random
class. – In fact, code such as this: new Random().Next(…)
is code smell, since it mis-uses the Random
class: you are not supposed to generate a new instance for every call; instead, you should reuse the same instance to generate a sequence of random numbers.
Unfortunately, you cannot simply use the same Random
instance across different concurrent tasks, since the relevant method is not thread safe – that is, calling it from several threads concurrently could lead to race conditions. There are several ways of getting around this, but the most fool-proof way is to use an explicit lock:
public Class(Random rng) {
lock (rng) {
var timeout = rng.Next(5000);
Debug.Print(timeout.ToString());
}
}
It’s important to note that every access to rng
must be locked, otherwise this is moot.
Now you can create your tasks and run them, and get proper randomness:
var rng = new Random();
var tasks = new [] {
Task.Run(() => { new Class(rng); }),
Task.Run(() => { new Class(rng); })
};
Task.WaitAll(tasks);
Note that when leaving out the lock(…)
block, it may seem like you are getting correct results. This is the danger of working with concurrency and randomness: it is hard to verify whether your result is actually correct or whether it was corrupted along the way. So tread with caution.