我有一个可以嵌套得很深的递归算法(它是反编译器的一部分)。我知道通常情况下您不会仅仅增加堆栈大小,因为堆栈溢出通常表示无限递归,但在这种情况下,算法有时可能只需要更大的堆栈,因此我在一个子线程中运行该算法,其堆栈大小可以通过CLI标志来增加:
fn main() -> Result<(), Box<std::io::Error>> {
// Process arguments
let args: Cli = Cli::from_args();
let child = thread::Builder::new()
.name("run".into())
.stack_size(args.stack_size * 1024 * 1024)
.spawn(move || -> Result<(), Box<std::io::Error>> { run(args)?; Ok(()) })
.unwrap();
child.join().unwrap()?;
Ok(())
}
fn run(args: Cli) -> Result<(), Box<std::io::Error>> {
...
}这很有效,将--stack-size=20传递给应用程序,run线程将获得一个20MB的堆栈,只要足够,它就会愉快地运行。
除非是第一次运行它,只使用8MB的默认堆栈,并且您会得到以下错误:
thread 'run' has overflowed its stack
fatal runtime error: stack overflow我想捕捉这个错误,而不是打印一条消息,提醒用户他们可以传递--stack-size=X来为反编译器提供更大的堆栈。
如何捕获Rust子线程的堆栈溢出?
发布于 2021-06-18 13:25:28
“中止死机”:
堆栈溢出属于我见过的被称为“中止死机”的类别。它们不能被捕获,也不能通过使用catch_unwind()恢复。OP建议使用子进程将故障与应用程序的其余部分隔离,这似乎是一种合理的解决方法。
Reddit上有一个很好的long thread,它谈到了“堆栈探测”(还有其他东西)。这可能是防止线程溢出的一种方法。如果您想了解更多信息,这里有Module compiler_builtins::probestack的文档。
下面是该参考文献的几个摘录:
堆栈探测的目的是提供一种静态保证,即如果线程具有保护页,则保证堆栈溢出将命中该保护页。最后,值得注意的是,在撰写本文时,LLVM只支持x86和x86_64上的堆栈探测。
有一个警告是必要的。我已经提到堆栈探测功能并不是完全安全的。对于大多数应用程序来说,这可能无关紧要,但对于通过网站自动化提供的编译器来说,这可能是无关紧要的。
避免递归
递归算法可以更容易编码,但在许多情况下比循环遍历树等数据结构的效率要低。循环方法更难编码,但速度更快,使用的内存更少。如果树遍历是正在解决的问题,那么在线上有很多示例可供参考。避免递归Some algorithms使用它们自己的编程声明的堆栈,例如向量、列表或其他类似堆栈的结构。其他算法,如Morris Traversal,不需要维护堆栈数据结构。返工有问题的递归逻辑是减少堆栈溢出机会的一种方法。
实现您自己的堆栈
有关如何在Python语言中将递归函数转换为迭代函数的语言不可知的示例,请参阅here's a generic approach。我在遇到递归多键快速排序代码的堆栈问题后写下了这个答案,我将其转换为Rust。我用它来对后缀数组进行排序,这需要成千上万的深度递归调用。在我按照所描述的方法进行转换后,应用程序能够毫无问题地处理非常大的文本块。
用于非致命可恢复死机的:
如果死机不是致命的/不可恢复的,则可以使用std::panic机箱捕获它们并获取诊断信息。
有关控制恐慌的更多信息可以在Rust Edition Guide的Controlling panics with std::panic部分找到。这里有一个指向std::panic文档的link。
use std::env;
use std::thread;
use std::panic::catch_unwind;
use std::panic;
fn main() -> Result<(), Box<std::io::Error>> {
let args = env::args().collect::<Vec<String>>();
panic::set_hook(Box::new(move |panic_info| {
match panic_info.location() {
Some(loc) => {
println!("Panic in file {} line {}.", loc.file(), loc.line());
},
None => { println!("No panic info provided..."); },
}
}));
let child = thread::spawn(move || {
catch_unwind(|| {
run(args);
});
});
child.join();
Ok(())
}
fn run(args: Vec<String>) -> Result<(), Box<std::io::Error>> {
println!("Args from command line: {:?}", args);
panic!("uh oh!");
Ok(())
}https://stackoverflow.com/questions/68029361
复制相似问题