使用递归时是否有关于如何避免堆栈溢出的一般规则?
您能够递归多少次取决于:
Guid
局部变量的方法将比没有任何局部变量的方法占用更多堆栈)如何避免堆栈溢出?不要递归太远:)如果你不能合理地确定你的递归会在没有走太远的情况下终止(我会担心“超过10”,尽管这很安全),那么重写它以避免递归。
这实际上取决于您使用的递归算法。 如果是简单的递归,你可以这样做:
public int CalculateSomethingRecursively(int someNumber)
{
return doSomethingRecursively(someNumber, 0);
}
private int doSomethingRecursively(int someNumber, int level)
{
if (level >= MAX_LEVEL || !shouldKeepCalculating(someNumber))
return someNumber;
return doSomethingRecursively(someNumber, level + 1);
}
值得注意的是,这种方法实际上仅在递归级别可以定义为逻辑限制的情况下才有用。 如果这种情况无法发生(例如分而治之算法),您将必须决定如何平衡简单性与性能与资源限制。 在这些情况下,一旦达到任意预定义的限制,您可能必须在方法之间切换。 我在快速排序算法中使用的一种有效方法是将其作为列表总大小的比率来执行。 在这种情况下,逻辑限制是条件不再最优时的结果。
我不知道有任何“硬集”来避免堆栈溢出。我个人尽力确保 -
1. 我的基本情况是正确的。
2. 代码在某个时刻到达基本情况。
特别是如果您正在进行多级递归(A->B->C->A->B...),您可能会发现可以将其中一个级别提取到循环中并节省一些内存。
大多数递归算法都可以迭代地表达。
有多种方法可以增加分配的堆栈空间,但我宁愿让您先找到一个迭代版本。 :)
幸运的是,当您使用正确的构造函数创建新线程时,您可以指定堆栈大小。根据我的经验,这很少有必要,但我见过一个案例,这就是解决方案。
您可以编辑二进制文件本身的 PE 标头来更改默认大小。如果您想更改主线程的大小,这非常有用。否则,我建议在创建线程时使用适当的构造函数。
因此,如果您认为在正常操作中可能会出现堆栈溢出,那么您需要考虑采用不同的方法来解决该问题。
将递归函数转换为迭代函数并不困难,尤其是 C# 具有 Generic::Stack 集合。使用 Stack 类型会将使用的内存移至程序的堆而不是堆栈中。这为您提供了存储递归数据的完整地址范围。如果这还不够,将数据分页到磁盘也不是太困难。但如果你到了这个阶段,我会认真考虑其他解决方案。