我是C#的新手,我正在创建一个赛道模拟器,但是我在运行我的代码时遇到了一些问题。我有一个由四个Greyhound对象组成的数组,如果我在我的form1.cs上调用Greyhound.Run并且我的Run方法上没有“MessageBox.Show(”Distance“+ distance)”,这表明我有多少每个灰狗应该移动的像素,所有的灰狗最终移动相同的距离。我不明白为什么会发生这种情况
namespace Race
{
class Greyhound
{
public int StartingPosition;
public int RacetrackLength;
public PictureBox MyPictureBox = null;
public int Location = 0;
public Random Randomizer;
public bool Run()
{
Point p = MyPictureBox.Location;
if (p.X + MyPictureBox.Width >= RacetrackLength)
{
//TakeStartingPostion();
return true;
}
else
{
Randomizer = new Random();
int distance = Randomizer.Next(100);
MessageBox.Show("Distance is " + distance);
p.X += distance;
MyPictureBox.Location = p;
return false;
}
}
public void TakeStartingPostion()
{
Point P = MyPictureBox.Location;
P.X = StartingPosition;
MyPictureBox.Location = P;
}
}
}
namespace Race
{
public partial class Form1 : Form
{
Guy[] guys = new Guy[3];
Greyhound[] hounds = new Greyhound[4];
public Form1()
{
InitializeComponent();
hounds[0] = new Greyhound()
{
StartingPosition = 12,
MyPictureBox = GreyHound1,
RacetrackLength = 636
};
hounds[1] = new Greyhound()
{
StartingPosition = 12,
MyPictureBox = GreyHound2,
RacetrackLength = 636
};
hounds[2] = new Greyhound()
{
StartingPosition = 12,
MyPictureBox = GreyHound3,
RacetrackLength = 636
};
hounds[3] = new Greyhound()
{
StartingPosition = 12,
MyPictureBox = GreyHound4,
RacetrackLength = 636
};
}
private void button2_Click(object sender, EventArgs e)
{
for (int i = 0; i < hounds.Length; i++)
{
hounds[i].Run();
}
}
}
}
答案 0 :(得分:1)
这是因为每当new Random()
方法点击Run()
块时,您就会调用else
。默认的Random
构造函数根据当前时间初始化伪随机数生成器。当没有中断时,所有4种方法都在“同一时间”运行,因此它们会吐出相同的随机数。要解决这个问题,要么只创建一个Random
,要么为每个种子使用不同的种子(使用以种子作为参数的Random
构造函数。)
这样的事情会起作用:
public class Greyhound
{
public static Random randomizer = new Random();
// ... In the run method ...
int distance = Greyhound.randomizer.Next(100);
}
更新:正如Groo指出的那样,如果您实际上是从多个线程调用Next()
,那么我所显示的代码不是线程安全的。虽然您的代码不是这种情况,但最好早点而不是稍后知道这类问题。解决此(潜在)问题的一般方法是使用Next()
将lock
的呼叫包围起来,如下所示:
// ... After the declaration of randomizer ...
private static object randomLock = new object();
// ... New Next() call...
lock (randomLock)
Greyhound.randomizer.Next(100);
答案 1 :(得分:0)
由于您可能会连续快速调用所有对象的Run
方法,因此Random
类的每个实例都会实例化with the same seed,并返回相同的伪随机数。< / p>
您可以通过创建一个具有单个(Singleton)Random
实例的静态随机类来解决此问题,确保每个调用者连续获得下一个数字。
对于线程安全有一些锁定,它看起来像:
public class StaticRandom
{
private static readonly Random _r = new Random();
private static object _lock = new object();
public static int Next(int max)
{
lock (_lock)
return _r.Next(max);
}
}
然后在不实例化的情况下使用它:
// no need for the "Randomizer" field anymore
int distance = StaticRandom.Next(100);
Jon Skeet在他的miscutil库中有完整的实现,还有一些usage info。