Reputation: 6547
I'm using to following xaml to create a simple racer app
<StackPanel>
<Slider x:Name="racerOne" Maximum="1000"/>
<Slider x:Name="racerTwo" Maximum="1000"/>
<Button Content="Start Race" Click="myButton_Click"/>
</StackPanel>
I use the following code
private void myButton_Click(object sender, RoutedEventArgs e)
{
Task firstRacer = Task.Run(() => Race(racerOne));
Task secondRacer = Task.Run(() => Race(racerTwo));
}
private void Race(Slider racer)
{
int step = 0;
while (step < 1000)
{
step += new Random().Next(0, 10);
Dispatcher.BeginInvoke(new ThreadStart(() => racer.Value = step));
Thread.Sleep(new Random().Next(0, 300));
}
}
Most of the times (let's say 90% precent) both sliders appear to move together, while in debug I can see each thread generates different values for step
. How come?
Upvotes: 3
Views: 109
Reputation: 1063058
Random
is seeded by the clock; you might want to do something like:
Random rand1 = new Random();
Random rand2 = new Random(rand1.Next());
Task firstRacer = Task.Run(() => Race(racerOne, rand1));
Task secondRacer = Task.Run(() => Race(racerTwo, rand2));
private void Race(Slider racer, Random rand)
{
int step = 0;
while (step < 1000)
{
step += rand.Next(0, 10);
Dispatcher.BeginInvoke(new ThreadStart(() => racer.Value = step));
Thread.Sleep(rand.Next(0, 300));
}
}
This creates two separate Random
instances with different seeds (by using the 1st to seed the second), and then passes these into the Race
as arguments. This removes any risk of overly-predictable behaviour due to timing.
Upvotes: 5
Reputation: 109607
When you create a new Random
object, it seeds it from the system clock. This has a resolution of only a few milliseconds, so if you create a new Random
object more frequently than that, it will produce the same random sequence as the previous one.
The solution is to create only ONE random object (and use a lock
to serialise access to it if more than one thread is accessing it at once).
Upvotes: 1
Reputation: 8280
You should initialize your random generator outside of the loop.
var rand = new Random();
while (step < 1000)
{
step += rand.Next(0, 10);
Dispatcher.BeginInvoke(new ThreadStart(() => racer.Value = step));
Thread.Sleep(rand.Next(0, 300));
}
For more details please go through this Jon Skeet's article: https://msmvps.com/blogs/jon_skeet/archive/2009/11/04/revisiting-randomness.aspx
Almost every Stack Overflow question which includes the words "random" and "repeated" has the same basic answer. It's one of the most common "gotchas" in .NET, Java, and no doubt other platforms: creating a new random number generator without specifying a seed will depend on the current instant of time. The current time as measured by the computer doesn't change very often compared with how often you can create and use a random number generator – so code which repeatedly creates a new instance of Random and uses it once will end up showing a lot of repetition.
Upvotes: 1