Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# A调用B,其中B调用一个巨大的堆栈跟踪_C#_Methods_Stack Trace - Fatal编程技术网

C# A调用B,其中B调用一个巨大的堆栈跟踪

C# A调用B,其中B调用一个巨大的堆栈跟踪,c#,methods,stack-trace,C#,Methods,Stack Trace,我有一个类,它有两个类似的方法: int counter = 0; private void A () { Debug.Log (System.Environment.StackTrace); counter++; B (); } private void B () { counter++; if (counter < 100) A (); } A() which calls... B() which calls... A()

我有一个类,它有两个类似的方法:

int counter = 0;

private void A ()
{
    Debug.Log (System.Environment.StackTrace);
    counter++;
    B ();
}

private void B ()
{
    counter++;

    if (counter < 100)
        A ();
}
A() which calls...
B() which calls...
A() which calls...
B() which calls...
A() which calls...
B() which calls...
....
int计数器=0;
私有无效A()
{
Debug.Log(System.Environment.StackTrace);
计数器++;
B();
}
私有空间B()
{
计数器++;
如果(计数器<100)
A();
}
这可以很好地编译和工作,并在100次迭代后按预期停止。然而,我有点担心,在它结束时,堆栈跟踪会变得相当巨大

我试图寻找这种模式,但实际上什么也找不到。一般来说,这样做是不可以的,还是可以的,只要它正确终止


编辑:关于这个用法的澄清:这实际上是我程序的主循环,应该永远保持下去;它不是任何类型的递归函数。基本上,我有一个大循环,但由于各种原因,我将它分成多个较小的方法,它们以这种方式相互调用。从功能上讲,这与将所有代码放在一个巨大的while循环中是一样的,但我担心的是堆栈跟踪。

这是一种递归模式,本身并不是一个糟糕的设计。只要递归调用生成的堆栈(取决于调用数和每个调用的参数数)不超过最大堆栈大小(对于.NET应用程序,默认为1MB),就可以了

如果它确实超过了它,您将不得不编写(必然存在的)命令等价物

注:


与堆栈大小相比,100个函数调用算不了什么。

您所做的是两步递归调用,与扫描目录结构非常相似

伪代码:

Function ScanDirectory CurrentDirectory

    DirectoryList.Add CurrentDirectory

    For each Directory in CurrentDirectory

        ScanDirectory Directory
但是递归函数总是必须得到很好的控制,因为如果递归没有限制,你会得到一个奇妙的StackOverflow异常

目录扫描非常适合查看这些密件,文件系统不允许深度超过256,因此可以确保递归限制为256


如果您担心可能的溢出,那么您必须实现一些技术来削减调用堆栈,根据示例,而不是直接调用可以将其添加到线程池中的工作中的方法,这样每个调用都将位于不同的线程中,并且每次调用中的堆栈都将为空(这只是一个非常脏的示例;))

这是递归——一种非常常见的编程技术。不同方法相互调用的一种典型情况是:在这种情况下,当解析的表达式有大量嵌套时,堆栈也会变得相当深

当决定递归的可用性时,你应该考虑最坏情况下调用的最大深度,作为输入大小的函数。如果函数按对数增长,递归很可能是可行的。如果它是线性增长的,并且输入可能有数千项,递归几乎肯定会因堆栈溢出而崩溃


一个重要的例外是:may编译器将对其进行优化,让具有线性堆栈要求的算法运行到最后。

每次调用一个方法时,该方法都会被放在堆栈跟踪上。这样,当当前方法返回时,它知道返回到哪里

在代码示例中,您正在递归调用方法
A()
,如其他答案中所述。如果仔细查看堆栈跟踪,您会注意到它的外观如下:

int counter = 0;

private void A ()
{
    Debug.Log (System.Environment.StackTrace);
    counter++;
    B ();
}

private void B ()
{
    counter++;

    if (counter < 100)
        A ();
}
A() which calls...
B() which calls...
A() which calls...
B() which calls...
A() which calls...
B() which calls...
....
堆栈跟踪大于预期的原因可能是每次调用方法
B()
时都调用了方法
A()

大堆栈跟踪是一个问题吗?可能不会。在32位系统上,堆栈指针为32位。这意味着您的堆栈框架上理论上最多可以有2^32个项目

如果使用正确,递归没有什么错。只要你有一个加伦特结束条款,你就可以了。在您的代码示例中,它是:

if (counter < 100)
if(计数器<100)
当递归发生时,您确实需要记住两件事

代码可读性

要确保其他程序员很容易理解为什么需要递归以及是什么导致递归,这是非常困难的。例如,在您的代码示例中,有方法
A()
调用方法
B()
,然后调用方法
A()
。这很令人困惑。通常,当使用递归时,方法应该调用自身,从而创建易于理解的代码。例如,这在快速排序样式的函数中非常常见

避免使用.NET

递归很容易失控并导致上述异常。因此,您必须能够保证end子句是可靠的


在您发布的代码示例中,如果在方法
A()
中将代码更改为
计数器--
,则可能存在问题。这可能是一个简单的错误,因为end子句依赖于其他方法正在执行的操作。其他程序员,甚至是你自己,在一段时间后,可能会出于某种原因不经意地更改此代码。这将返回到代码可读性。尽量将end子句保留为递归本身,从而限制这一潜在问题。

这就是递归的工作原理这看起来像是一个精心设计的示例-递归是一种非常好的模式,但它通常用于处理“树形”问题,因此“深度”通常只有几个级别。100级递归似乎有点重-即使对10亿个数据项使用递归排序,通常也不会创建超过30的堆栈深度。我想,为了了解您有多少堆栈空间,可以将最大迭代次数设置为100、1000、10000,等等。我的猜测是,在堆栈溢出之前,堆栈可能会变得相当大。添加了一个编辑,以澄清我实际使用它的目的;它不是一个真正的递归函数,你应该