我正在重构和扩展一个基于C#代理的小型模型,以帮助一些生物学教授预测疾病的传播。模拟的每一年,每个单独的代理人随机地前往附近的人口节点,可能传播疾病。我是C#的新手,但是我已经读过Random.Next的潜在问题,如果使用相同的系统时间重新初始化,则返回相同的值。为了避免这种情况,我创建了一个为每个新随机值引用的静态实例。
细节:
在我扩展模型的努力中,我改变了它以并行计算每个人口节点的“旅行”信息。在测试模型时,我注意到在新版本中疾病不会在第一年过去。进一步的调查将问题缩小到节点之间的旅行。第一年后,所有代理人都保持不动。我检查了负责他们旅行的功能,发现它的工作原理是创建一个附近所有节点的列表,生成一个随机数< =列表中的元素数,然后前往listOfNearbyNodes [myRandomNumber]。
问题:
然后我添加了一个print语句来输出每次迭代的随机索引的值。我发现整个模型在模拟的第一年完全按预期工作,随机数生成在可接受的范围内。但是,在第一年结束并且模拟循环后,完全相同的代码将仅返回0的“随机”索引。每个线程,每个迭代,每个节点,每个代理程序始终为0.因为代理程序的当前节点始终是列表中的第一个项目代理商再也不会移动。
我认为这可能是系统时间种子错误的另一种表现,所以我尝试了三种不同的方法来实现静态随机对象,但它没有帮助。每次运行模拟时,第一年总是正常工作,然后Random.Next()开始只返回0。
有没有人有关于我应该在下一步看哪个bug的想法?谢谢!
答案 0 :(得分:20)
我怀疑你在多个线程中同时使用Random
的同一个实例。不要这样做 - 它不是线程安全的。
选项:
Random
的新实例(ThreadStatic
可以在此处提供帮助)Random
的单个实例,但只能在锁中使用它。我有blog post一些示例代码,但请阅读评论以及改进它的好建议。我打算在近期的某个时候撰写另一篇关于随机性的文章......
答案 1 :(得分:6)
我不相信Random类被设计为线程安全的(可以从多个线程同时使用) - 所以如果你以这种方式共享一个实例,你可能会破坏随机生成器的状态,防止它运作正常。
您可以将包含Random类引用的静态变量装饰为ThreadStatic
,这将允许您为每个线程维护一个单独的实例:
[ThreadStatic]
private static Random m_Random; // don't attempt to initialize this here...
public void YourThreadStartMethod()
{
// initialize each random instance as each thread starts...
m_Random = new Random();
}
如果您使用的是.NET 4.0,那么还有ThreadLocal<T>
类,这有助于更轻松地为每个线程初始化一个实例。
答案 2 :(得分:2)
Random
对象不是线程安全的。要解决这个问题,您可以使用从this answer
class ThreadSafeRandom
{
private static Random random = new Random();
public static int Next()
{
lock (random)
{
return random.Next();
}
}
}
您还可以使用RNGCryptoServiceProvider
,它是线程安全的,并且还可以生成更好的随机数据。
答案 3 :(得分:1)
我认为您应该使用RNGCryptoServiceProvider
http://msdn.microsoft.com/en-us/library/system.security.cryptography.rngcryptoserviceprovider.aspx