堆栈溢出的可能原因

时间:2015-02-26 14:08:37

标签: c# 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次时,这很有效,但之后它会立即给出堆栈溢出异常并停止。我不明白为什么递归不是无限的。

2 个答案:

答案 0 :(得分:4)

这是因为以下情况:

  1. 您生成一个随机数
  2. 假设第一个if中没有条件满足,else子句被执行
  3. currentTrack已添加到playlist
  4. 您可以多次调用该函数,这会导致几乎所有使用范围[1,30]的数字
  5. 这增加了满足第一个条件的机会
  6. 这使得对于许多连续的getNextTrack轨道的递归调用已经存在。
  7. 可能导致堆栈溢出。
  8. 请注意,数字将在大多数时间重复,因为您继续使用新的随机生成器。 Random构造函数的The documentation说:

      

    默认种子值源自系统时钟并具有有限的分辨率。因此,通过调用默认构造函数紧密连续创建的不同Random对象将具有相同的默认种子值,因此将生成相同的随机数集。使用单个Random对象生成所有随机数可以避免此问题。您还可以通过修改系统时钟返回的种子值,然后将此新种子值显式提供给Random(Int32)构造函数来解决此问题。有关更多信息,请参阅Random(Int32)构造函数。

    (强调我的)

    发生堆栈溢出所需的递归不需要在概念上无限。它足以超过堆栈上的空间限制。这也在the documentation中解释:

      

    执行堆栈溢出错误抛出StackOverflowException,通常是在非常深或无限递归的情况下。

    (强调我的)

答案 1 :(得分:4)

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

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

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

每次调用方法时重新创建Random 由于Random系统计时器初始化,它返回相同 价值一遍又一遍。补救措施:从方法中移动Random

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