2012-08-02 40 views
5

我遇到了一种情况,根据minidump,某些文件在递归下降解析器中导致堆栈溢出。不幸的是,我无法得到一个文件的例子,为了重现这个问题(客户端存在保密问题),这使得我在诊断当前真正的问题时有点困难。如何防止或恢复工作线程上的堆栈溢出?

显然,解析器需要一些注意,但现在我的首要任务就是保持程序运行。作为一项权宜之计,我能做些什么来阻止整个计划的实施?

我的第一选择是找到某种方式来预测我在堆栈中的空间不足,以便在发生溢出之前正常中止解析器。未能解析文件是可以接受的选项。第二种选择是让它发生,捕捉错误并记录下来,然后继续处理其余的数据。

解析发生在Parallel.ForEach()循环中。如果这有帮助,我愿意将其换为其他方法。

编辑:如果我能得到当前线程堆栈的大小和堆栈指针的位置,真正的杀手是什么。这可能吗?

编辑2:我终于设法拧出某人的样本文件并在调试器中捕获错误。事实证明,这不是属于我们的代码 - 例外发生在HtmlAgilityPack的某处。所以看起来我将不得不尝试找到完全不同的方法。

+0

不知道这是否会有帮助,因为究竟导致堆栈溢出的原因并不清楚(并发性不应该导致这种情况:递归可能),但是您是否尝试过使用ParallelOptions.MaxDegreeOfParallelism来限制并发调用的数量? – Jcl 2012-08-02 19:11:42

+0

一种选择是跟踪解析的当前“深度”,如果它太高,则保释。 – dlev 2012-08-02 19:13:33

+0

@dlev虽然我想了解更多细节。 .NET文档表明,但如何选择合适的最大深度,因为堆栈框架和调用堆栈整体可以具有不同的大小? – 2012-08-02 19:15:19

回答

3

堆栈在桌面CLR上默认有1 MB限制,但是您的can increase it

您可以使用continuation passing style来使用堆而不是堆栈。

在C#5.0中,有编译器提供的异步机制来自动执行此过程。我还没有尝试过这个最新版本。正如Alex所提到的那样,在C#中不支持尾部调用优化,这可能足以使F#解析问题的原因。这里的some material on lexing and parsing with F#. YMMV,如演示this article.

您还需要图循环检测,使您的程序在presence of bad inputs固体。

作为一种收集更多信息的方式,您可以通过累加器整数来跟踪您的调用堆栈的深度。这不会直接转化为所述调用堆栈消耗的内存,但是它会给出一个总体思路。例如,当该数字大于某个用户可配置或预定义的阈值时,您可以抛出并捕获您自己的异常。

public void Recursive(int acc) 
{ 
    if (acc > myLimit) 
     throw new MyOverflowException(acc); 

    Recursive(acc+1); 
} 

,然后在调用点:

try { Recursive(0); } catch (MyOverflowException) { /* handle it*/ } 

按照要求,我会埃里克利珀上this very topic.

+1

有一点细节会很好。 – 2012-08-02 19:14:02

+0

@GregC这是我正在考虑做的一个长期解决方案。但是现在我正在寻找一个权宜之计,那将是一个相当大的重构。 – 2012-08-02 19:17:46

+1

我的意思是给出一个延续传球样式的例子,甚至可以演示如何使用较少的叠加。 – 2012-08-02 19:23:01

0

线程链接您的神话般的博客崩溃由于SOE将会降低整个过程,并且你可以做的事情也不多。

作为恢复措施,您可以将解析器作为单独的进程启动,并设置IPC机制与子进行通信。这样,孩子进程就可以自由死亡而不会影响主进程。