【问题标题】:Informative exit on stack overflow in worker thread工作线程中堆栈溢出的信息退出
【发布时间】:2014-12-03 14:43:15
【问题描述】:

我正在编写一个 C++ 程序,它将在递归数据上运行一堆工作线程,这样即使我增加了默认堆栈空间,线程也可能会遇到堆栈溢出。

理想的做法是让堆栈根据需要动态扩展,但如果无法做到这一点,则程序失败是可以接受的,并且用户在使用更大的堆栈大小重新编译后重试是可以接受的。

程序崩溃而没有错误消息的默认行为的问题是用户无法知道问题是什么或如何处理它;就用户所知,程序可能试图除以零或取消引用空指针;所以如果程序必须崩溃,我希望它首先将“堆栈溢出”打印到标准错误。

显然,在便携式 C++ 中不会有解决方案,但我会很高兴有一个适用于 Windows 的解决方案和另一个适用于 Linux 的解决方案。

寻找在 Windows 上让程序退出并显示信息丰富的错误消息的方法,我一直在阅读有关向量化和结构化异常处理的文档;一个问题是这些似乎是线程本地的,并且线程不能安全地写入stderr;充其量你会得到一个比赛条件。

有没有已知的处理方法?

【问题讨论】:

  • 我认为您可能试图以错误的方式解决此问题。如果递归算法有可能(不是经常)溢出堆栈,那么您应该将其重写为迭代。
  • @sjdowling 好吧,这并不频繁,因为我增加了堆栈大小;并且数据本质上是递归的。避免使用机器堆栈只会意味着我自己要重新实现递归,这会更慢,而且会让代码变得一头雾水。
  • 但是如果你自己实现递归,当你用完它的空间时,你肯定会优雅地失败。此外,您不会很快失败,因为存储不需要是连续的。
  • @HarryJohnston 是的。尽管如此,代码既是速度关键(首先需要多个线程的原因),又是棘手且难以正确处理。首先,我愿意看看在事情开始破裂之前我可以将筹码量提高到多高。
  • [在注意到您在问题中已经提到 SEH 后,我删除了我的最后一条评论。] 无论如何,SEH 应该可以解决您的问题,您只需要正确实施即可。在线程中捕获(结构化)异常后,将消息添加到线程安全队列和/或使用独特的错误代码退出线程。

标签: c++ linux windows multithreading stack-overflow


【解决方案1】:

操作系统(至少是 Linux 或 Unix 版本)允许您捕获堆栈错误。

类似这样的:

 // Note: Calling printf here is probably not a brilliant idea, 
 // as we're in a signal handler. It is NOT well-defined what happens.
 void handler(int arg)
 {
    fprintf(stderr, "Crashed due to signal handler\n"); 
    exit(42);
 }

然后在 main 或类似的...

 struct sigaction sa = { handler, NULL, 0, 0, NULL };
 struct sigaction oldsa;
 sigaction(SIGSTKFLT, sa, oldsa);

我将尝试提出更多“完整”的解决方案,并进行一些实验。

(我相信可以替换堆栈,但我认为你不能以一种有意义的方式实际继续到那个时候,只是让你以一种比简单崩溃更理智的方式恢复!)

这似乎可以工作:

#include <signal.h>
#include <unistd.h>
#include <iostream>
#include <cstdlib>

void handler(int arg)
{
    write(2, "stack overflow\n", 15);
    _exit(42);
}

void* duh(void *arg)
{
    if(duh(arg))
    {
        return duh(NULL);
    }
    else
    {
        return duh(arg);
    }
}

void* crash_wrapper(void *arg)
{
    static char stack[SIGSTKSZ];
    stack_t ss = {};
    ss.ss_sp = stack;
    ss.ss_size = SIGSTKSZ;
    sigaltstack(&ss, 0);

    struct sigaction sa = {};
    sa.sa_handler = handler;
    sa.sa_flags = SA_ONSTACK,
    sigfillset(&sa.sa_mask);
    sigaction(SIGSEGV, &sa, 0);

    return duh(arg);
}

int main()
{
    pthread_t t;
    int status = pthread_create(&t, 0, crash_wrapper, 0 );

    for(;;)
    {
        std::cout << "Still going..." << std::endl;
        sleep(1);
    }
}

我对处理程序中的write 并不完全满意,但我尝试过的所有其他方法似乎也不起作用...... :(

【讨论】:

  • 对,这是一个开始,尽管正如您所说,从信号处理程序调用 printf 是有问题的。嗯,想一想,换成write(2, "stack overflow", 14)会安全吗?
  • 可能没有那么好,真的。我正在看其他东西,我认为它会起作用。
  • 信号处理程序能否将消息添加到线程安全队列中,以供主线程检测和响应?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-26
  • 2015-09-08
  • 2019-05-18
  • 2014-01-21
  • 2011-05-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多