堆栈溢出的可能原因

Possible Reasons for Stack Overflow

我有以下 C# 代码,它用 1 到 30 之间的随机数填充 ArrayList。 我必须调用此函数 25 次。 我的代码:

    private void getNextTrack()
    {
        int currentTrack = new Random().Next(1, 30);
        if (playlist.Contains(currentTrack) || (topicNo == 8 && currentTrack == 29) || (topicNo == 3 && currentTrack == 14))
            getNextTrack(); //If track already exsits or the 2 specified topics dont have that track no. then try again.

        else
        {
            playlist.Add(currentTrack);
            ++tracksPlayed;
        }
    }

当函数最初被调用 10-11 次时效果很好,但之后它立即给出堆栈溢出异常并停止。我不明白为什么递归不是无限的。

发生这种情况是因为以下情况:

  1. 您生成一个随机数
  2. 假设满足第if条中的第none条条件,执行else
  3. currentTrack 添加到 playlist
  4. 您多次调用该函数,这导致 [1, 30] 范围内的几乎所有数字都被使用
  5. 这增加了满足第一个条件的机会
  6. 这使得 getNextTrack 的许多连续递归调用成为可能,轨道已经存在。
  7. 这可能会导致堆栈溢出。

请注意,数字 大部分时间都会重复 ,因为您一直在使用新的随机生成器。 Random 构造函数的 The documentation 说:

The default seed value is derived from the system clock and has finite resolution. As a result, different Random objects that are created in close succession by a call to the default constructor will have identical default seed values and, therefore, will produce identical sets of random numbers. This problem can be avoided by using a single Random object to generate all random numbers. You can also work around it by modifying the seed value returned by the system clock and then explicitly providing this new seed value to the Random(Int32) constructor. For more information, see the Random(Int32) constructor.

(强调我的)

发生堆栈溢出所需的递归在概念上不需要是无限的。它超过堆栈上 space 的限制就足够了。这也在 :

中解释

WhosebugException is thrown for execution stack overflow errors, typically in case of a very deep or unbounded recursion.

(强调我的)

堆栈溢出的原因在第1行:

  private void getNextTrack() {
    int currentTrack = new Random().Next(1, 30); // <- That's the cause

    if (playlist.Contains(currentTrack) ...)
      getNextTrack(); 

您每次调用该方法时都重新创建 Random 并且 因为 Random 系统定时器 初始化,它 returns 与 相同 value 一遍又一遍。补救措施:从方法中移动 Random

// Simplest, not thread-safe
private static Random generator = new Random(); 
...
private void getNextTrack()
{
   int currentTrack = generator.Next(1, 30);
   ...