在多个线程中使用Random共享相同的方法

时间:2013-11-14 09:56:50

标签: c# wpf multithreading dispatcher

我正在使用xaml来创建一个简单的赛车应用

<StackPanel>
    <Slider x:Name="racerOne" Maximum="1000"/>
    <Slider x:Name="racerTwo" Maximum="1000"/>
    <Button Content="Start Race" Click="myButton_Click"/>
</StackPanel>

我使用以下代码

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

大多数时候(比如90%的前提)两个滑块似乎一起移动,而在调试中我可以看到每个线程为step生成不同的值。怎么样?

3 个答案:

答案 0 :(得分:5)

Random由时钟播种;你可能想做类似的事情:

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

这会创建两个单独的Random实例,其中包含不同的种子(通过使用1st来播种第二个种子),然后将这些实例作为参数传递给Race。这消除了因时间问题而导致过度预测行为的风险。

答案 1 :(得分:1)

您应该在循环之外初始化随机生成器。

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

有关详细信息,请阅读Jon Skeet的文章: https://msmvps.com/blogs/jon_skeet/archive/2009/11/04/revisiting-randomness.aspx

  

几乎每个Stack Overflow问题都包含“随机”字样   并且“重复”具有相同的基本答案。这是最常见的一种   .NET,Java中的“陷阱”,毫无疑问是其他平台:创建新的   没有指定种子的随机数生成器将取决于   目前的时间。由计算机测量的当前时间   与您创建和创建的频率相比,经常不会发生变化   使用随机数生成器 - 所以代码重复创建一个新的   随机的一个实例并使用它一次将最终显示很多   重复。

答案 2 :(得分:1)

当您创建一个新的Random对象时,它会从系统时钟中播种它。它的分辨率只有几毫秒,因此如果您创建一个新的Random对象的频率高于此值,它将产生与前一个相同的随机序列。

解决方案是只创建一个随机对象(如果多个线程同时访问它,则使用lock来序列化对它的访问。)