我正在Unity(2017.1.1f1)内的.NET Next()
实例上调用System.Random
。它从受保护的函数Sample()中抛出一个IndexOutOfRangeException
,该函数不接受任何参数,并返回0到1之间的双精度值。
这里是例外情况
System.IndexOutOfRangeException:数组索引超出范围。
在System.Random.Sample()[0x0003e]中 /Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Random.cs:91
在System.Random.Next(Int32 maxValue)[0x00017]中的/Users/builduser/buildslave/mono/build/mcs/class/corlib/System/Random.cs:112
在Quicksilver.SysIRand.RandInt(Int32 max_exclusive)[0x00008]中位于F:\ SVNHome \ gemrush \ GemRush \ Assets \ Source \ Shared \ Utility \ IRand.cs:38
在Quicksilver.IEnumerableExt.SelectRandom [Skill](IEnumerable`1 集合,IRand兰特,Int32计数)[0x00070]在 F:\ SVNHome \ gemrush \ GemRush \ Assets \ Source \ Shared \ Utility \ IEnumerableExt.cs:61
(在此之上还有13层调用栈)
这是一个多线程环境,但是每个线程都有其自己的System.Random专用实例。从下面的代码中可以看到,传递给Next()的参数必须为1或更高。
此错误在复杂的自动化测试脚本中投入了大约1个小时,因此多次运行以测试理论是昂贵的,并且任何更改RNG调用方式的修改都将阻止复制。 (如果错误某种程度上涉及线程之间的意外交互,则可能根本无法再现。)
由于它已经进入测试脚本一个小时了,因此该方法的绝大多数调用都不得抛出错误。
直接使用随机数的函数在这里:
// Chooses count items at random from the enumeration and returns them in an array
// The order of selected items within the array is also random
// If the collection is smaller than count, the entire collection is returned (in random order)
public static T[] SelectRandom<T>(this IEnumerable<T> collection, IRand rand, int count = 1)
{
if (count <= 0) return new T[0]; // Optimization for trivial case
T[] keep = new T[count];
int found = 0;
foreach (T item in collection)
{
if (found < count)
{
// Take the first #count items, in case that's all there are
// Move a random item of those found so far (including the new one)
// to the end of the array, and insert the new one in its place
int r = rand.RandInt(found + 1);
keep[found++] = keep[r];
keep[r] = item;
}
else
{
// Random chance to replace one of our previously-selected elements
++found;
if (rand.RandInt(found) < count) // probability desired/found
{
// Replace a random previously-selected element
int r = rand.RandInt(count);
keep[r] = item;
}
}
}
if (found < count)
{
// The collection was too small to get everything requested;
// Make a new, smaller array containing everything in the collection
T[] all = new T[found];
Array.Copy(keep, all, found);
return all;
}
return keep;
}
此行抛出错误:
if (rand.RandInt(found) < count) // probability desired/found
IRand是围绕System.Random的非常薄的包装程序的接口; IRand.RandInt()仅返回Random.Next()。
编辑
以下是创建和分发随机实例的方式:
private void Start()
{
SysIRand[] rngs = new SysIRand[parallelTesters];
if (parallelTesters > 0) rngs[0] = new SysIRand(new System.Random(548913));
if (parallelTesters > 1) rngs[1] = new SysIRand(new System.Random(138498));
if (parallelTesters > 2) rngs[2] = new SysIRand(new System.Random(976336));
if (parallelTesters > 3) rngs[3] = new SysIRand(new System.Random(793461));
if (parallelTesters > 4) rngs[4] = new SysIRand(new System.Random(648791));
if (parallelTesters > 5) rngs[5] = new SysIRand(new System.Random(348916));
if (parallelTesters > 6) rngs[6] = new SysIRand(new System.Random(8467168));
if (parallelTesters > 7) rngs[7] = new SysIRand(new System.Random(6183569));
for (int i = 8; i < parallelTesters; ++i)
{
rngs[i] = new SysIRand(new System.Random(7 * i * i + 8961 * i + 129786));
}
for (int t = 0; t < parallelTesters; ++t)
{
SysIRand rand = rngs[t];
IBot bot = BotFactory.DrawBot(rand);
BotTester tester = new BotTester(rand, bot);
tester.testerID = t + 1;
tester.OnMessage += (str) => UponMessage(tester.testerID + " ~ " + str);
tester.OnError += (str) => UponError(tester.testerID + " ~ " + str);
tester.OnGameAborted += UponGameAborted;
tester.OnDebugMoment += UponDebugMoment;
testers.Add(tester);
}
(在此运行中,parallelTesters
的值为3)
每个BotTester
仅使用传递给其构造函数的Random实例。每个线程对一个单独的BotTester
都是私有的,从BotTester.RunGame()
开始:
public bool RunGame(GameMode mode, int maxGames = 1, long maxMilliSeconds = 100000000, int maxRetries = 5000)
{
if (threadRunning) return false;
thread = new Thread(() => ThreadedRunGame(mode, maxGames, maxMilliSeconds, maxRetries));
thread.Start();
return true;
}
答案 0 :(得分:2)
唯一有意义的解释是,您认为自己访问Random()
实例线程安全,每个线程都有自己的Random()
实例,但看起来您正在访问相同的{ {1}}实例仍在计算中间。这是Unity中的实现。 Random()
只需调用Sample()
InternalSample()
如您所见,您可以获得private int InternalSample()
{
int inext = this.inext;
int inextp = this.inextp;
int index1;
if ((index1 = inext + 1) >= 56)
index1 = 1;
int index2;
if ((index2 = inextp + 1) >= 56)
index2 = 1;
int num = this.SeedArray[index1] - this.SeedArray[index2];
if (num < 0)
num += int.MaxValue;
this.SeedArray[index1] = num;
this.inext = index1;
this.inextp = index2;
return num;
}
的位置是有限的,即当您访问IndexOutOfRangeException
时。这是SeedArray的定义。
this.SeedArray
它已经分配给56个元素,在执行public class Random
{
private int[] SeedArray = new int[56];
....
}
方法时,您可以看到每次调用InternalSample
和index1
始终被限制为最多{{1} },除非多次调用index2
方法。