多线程随机数生成器的奇怪行为
本文关键字:随机数生成器 多线程 | 更新日期: 2023-09-27 18:12:21
请查看下面的代码,此代码尝试计算生日冲突的可能性。令我惊讶的是,如果我按顺序执行这些代码,结果预计在0.44左右;但如果在PLinq上试用,结果是0.99。有人能解释一下结果吗?
public static void BirthdayConflict(int num = 5, int people = 300) {
int N = 100000;
int act = 0;
Random r = new Random();
Action<int> action = (a) => {
List<int> p = new List<int>();
for (int i = 0; i < people; i++)
{
p.Add(r.Next(364) + 1);
}
p.Sort();
bool b = false;
for (int i = 0; i < 300; i++)
{
if (i + num -1 >= people) break;
if (p[i] == p[i + num -1])
b = true;
}
if (b)
Interlocked.Increment(ref act);
// act++;
};
// Result is around 0.99 - which is not OK
// Parallel.For( 0, N, action);
//Result is around 0.44 - which is OK
for (int i = 0; i < N; i++)
{
action(0);
}
Console.WriteLine(act / 100000.0);
Console.ReadLine();
}
您正在使用(线程之间)共享实例System.Random
。它不是线程安全的然后你得到错误的结果(实际上它只是不工作,它会返回0)。
如果你的应用程序从多个线程调用随机方法,你必须使用一个同步对象来确保一次只有一个线程可以访问随机数生成器。如果不能确保以线程安全的方式访问Random对象,则调用返回随机数的方法返回0。
简单(但对并行执行来说效率不高)的解决方案是使用锁:
lock (r)
{
for (int i = 0; i < people; i++)
{
p.Add(r.Next(364) + 1);
}
}
为了提高性能(但您应该进行测量),您可以使用多个System.Random
实例,请注意使用不同的种子初始化每个实例。
我找到了一个有用的解释,为什么随机不能在多线程下工作,尽管它最初是为Java设计的,但仍然是有益的。