简体   繁体   English

如何防止或恢复工作线程上的堆栈溢出?

[英]How can I prevent or recover from a stack overflow on a worker thread?

I've run into a situation where, according to a minidump, certain files are causing a stack overflow in a recursive-descent parser. 我遇到过一种情况,根据minidump,某些文件在递归下降解析器中导致堆栈溢出。 Unfortunately I can't get my hands on an example of a file that does this in order to reproduce the issue (the client has confidentiality concerns), which leaves me a bit hamstrung on diagnosing the real problem for the moment. 不幸的是,我无法得到一个文件的例子,这样做是为了重现这个问题(客户端有保密问题),这让我在诊断真正的问题时有点麻烦。

Clearly the parser needs some attention, but right now my top priority is to just keep the program running. 很明显,解析器需要一些关注,但现在我的首要任务是保持程序运行。 As a stopgap measure, what can I do to keep this from bringing down the whole program? 作为权宜之计,我该怎样做才能阻止整个计划的落实?

My first choice would be to find some way to anticipate that I'm running out of room on the stack so that I can gracefully abort the parser before the overflow happens. 我的第一选择是找到某种方法来预测我在堆栈上的空间不足以便我可以在溢出发生之前优雅地中止解析器。 Failing to parse the file is an acceptable option. 无法解析文件是可以接受的选项。 The second choice would be to let it happen, catch the error and log it, then continue with the rest of the data. 第二种选择是让它发生,捕获错误并记录它,然后继续其余的数据。

The parsing is happening in a Parallel.ForEach() loop. 解析发生在Parallel.ForEach()循环中。 I'm willing to swap that out for some other approach if that will help. 如果有帮助,我愿意将其换成其他方法。

EDIT: What would be really killer is if I could just get the size of the current thread's stack, and the position of the stack pointer. 编辑:如果我能够获得当前线程堆栈的大小以及堆栈指针的位置,那么真正的杀手是什么。 Is this possible? 这可能吗?

EDIT 2: I finally managed to wring a sample file out of someone and trap the error in a debugger. 编辑2:我终于设法从某人那里取出一个样本文件并将错误记录在调试器中。 It turns out it's not code that belongs to us at all - the exception's happening somewhere in HtmlAgilityPack . 事实证明,它根本不属于我们的代码 - HtmlAgilityPack中的某个地方发生了异常。 So it looks like I'm going to have to try and find a completely different tack. 所以看起来我将不得不尝试找到一个完全不同的方法。

Stack has 1 MB limit by default on desktop CLR, but you can increase it . 默认情况下,桌面CLR上的堆栈限制为1 MB,但您可以增加它

You can use a continuation passing style to use heap instead of stack. 您可以使用延续传递样式来使用堆而不是堆栈。

In C# 5.0, there's async mechanism provided by compiler that automates this process. 在C#5.0中,编译器提供了异步机制来自动执行此过程。 I haven't tried this with the latest build. 我没有尝试使用最新版本。 As mentioned by Alex, there is no support for tail-call optimization in C#, and this might be big enough of a reason to adopt F# for parsing problems. 正如Alex所提到的,C#中不支持尾调用优化,这可能足以成为采用F#解析问题的理由。 Here's some material on lexing and parsing with F#. 这里有一些关于lexing和F#解析的资料。 YMMV, as demonstrated in this article. YMMV,如本文所示。

You'd also need graph cycle detection to make your program solid in the presence of bad inputs . 您还需要图形周期检测,以便在出现输入错误时使您的程序稳固。

As a way to collect more info, you can needle through an accumulator integer that tracks how deep is your call stack. 作为收集更多信息的一种方法,您可以通过累加器整数来跟踪调用堆栈的深度。 This will not directly translate into memory consumed by said call stack, but it gives you a general idea. 这不会直接转换为所述调用堆栈所消耗的内存,但它为您提供了一个大致的想法。 For example, you could throw and catch your own exception when that number is greater than some user-configurable or predefined threshold. 例如,当该数字大于某个用户可配置或预定义的阈值时,您可以抛出并捕获自己的异常。

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

    Recursive(acc+1);
}

and then at the call-site: 然后在呼叫现场:

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

As requested, I'll link you up to the fabulous blog by Eric Lippert on this very topic. 根据要求,我将链接到Eric Lippert关于这个主题的精彩博客

A thread crashing due to SOE will bring down the whole process and there's not much you can do about it. 由于SOE导致的线程崩溃将导致整个过程失败,并且您无法做到这一点。

As a recovery measure you could instead launch the parser as a separate process and set up an IPC mechanism to communicate with the child. 作为恢复措施,您可以将解析器作为单独的进程启动,并设置IPC机制以与子进程通信。 That way, the child process is free to die without impacting the main process. 这样,子进程可以自由死亡而不会影响主进程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何防止我的Ackerman函数溢出堆栈? - How can I prevent my Ackerman function from overflowing the stack? 如何将此C#工作线程代码与主线程中的共享数据变量分离? - How can I decouple this C# worker thread code from the main thread for shared data variables? 如何删除单独的工作线程 function 并创建一个线程 - How can I remove seperate worker thread function and create a thread 防止崩溃进程发生堆栈溢出异常 - Prevent Stack Overflow Exception from crashing process 如何在不挂起控制台应用程序的情况下从工作线程调用Windows PostMessage函数? - How can I call the windows PostMessage function from a worker thread without hanging the console application? 如何使用后台工作程序将参数(字符串)发送到UI线程? - How can I send arguments(string) to the UI thread with the Background Worker? 如何在多线程中从 TargetInvocationException 中优雅地恢复? - How to recover gracefully from TargetInvocationException in multi thread? 在c#中,如何从线程池中检查线程是工作线程还是I / O线程? - In c#, how to check the thread from threadpool is a worker thread or an I/O thread? 防止扫雷克隆中的堆栈溢出 - Prevent Stack Overflow in a Minesweeper Clone 如何将后台工作线程设置为单线程单元? - How can I make a background worker thread set to Single Thread Apartment?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM