C#:递归方法期间的堆栈溢出异常

Posted

技术标签:

【中文标题】C#:递归方法期间的堆栈溢出异常【英文标题】:C# : Stack Overflow Exception during recursive method 【发布时间】:2020-12-17 10:03:45 【问题描述】:

我有一个递归方法,只要随机生成的数字不等于 1,它就会调用自己。 我正在尝试测试不同事物的赔率,例如闪亮的口袋妖怪 (1/8192) 或 Minecraft 中的 12 眼种子 (10^12),即使我了解 Stack Overflow 发生的原因,但我不知道如何修复它。使用线程会大大降低速度(5000 次计算/秒,没有线程,大约 500 次)。

代码如下:

static void shiny()

    total = 0;
    counter += 1;
    resetcounter += 1;
    if (rdm.Next(8192) + 1 == 1)
    
        Console.WriteLine("SHINY !! In: " + counter + " resets.");
    
    else
    
        if (resetcounter > 7000)
        
            Console.WriteLine("Reset. Current: " + counter);
            ThreadStart newtask = new ThreadStart(shiny);
            Thread task = new Thread(newtask);
            task.Start();
        
        else
        
            Console.WriteLine("Reset. Current: " + counter);
            shiny();
        
    

我使用 resetcounter 变量来避免堆栈溢出错误,因为它发生在 7k 次“重置”左右,然后它启动一个新线程。 不过,我很想了解测试赔率如何避免堆栈溢出!

【问题讨论】:

为什么这是递归而不是带有中断条件的简单循环? 哦,我没想到,我猜我是小脑袋哈哈!循环确实有效,谢谢! 【参考方案1】:

了解一些背景信息。 C# 和许多其他语言在调用方法时使用call stack,这用于局部变量、返回值和其他内容。因此,当您调用一个方法时,堆栈的大小会增加,并在方法返回时减少相同的数量。最大大小通常为 1-4Mb,并且在使用没有明确定义的最大深度的递归代码时很容易达到。

递归函数可以重写为迭代函数。有些情况需要一个显式的stack,它可以更大,但在这种情况下不需要。示例代码,减去线程,可以重写如下:

    void shiny()
    
        while (rdm.Next(8192) != 0)
        
            counter += 1;
        
        Console.WriteLine("SHINY !! In: " + counter + " resets.");
    

虽然这样的实验很有趣,但您可以明确地计算概率。假设在一轮中找到一个闪亮的机会是 8192 分之一,或 0.012%,那么在 n 轮之后发现至少一个闪亮的机会将是 1 - (8191/8192)^n。把它扔到 wolfram alpha 中,你会得到一个 probability plot。

【讨论】:

以上是关于C#:递归方法期间的堆栈溢出异常的主要内容,如果未能解决你的问题,请参考以下文章

怎么防止堆栈溢出

由于递归方法调用导致 Java 堆栈溢出

函数 堆栈溢出

在c#中使用单一方法会发生***异常吗?

不使用递归如何抛出堆栈溢出异常?

C# setter 中的堆栈溢出异常