【问题标题】:Protecting main thread from errors in worker thread保护主线程免受工作线程中的错误
【发布时间】:2014-09-22 09:38:38
【问题描述】:

使用 posix 线程时,是否有某种方法可以“保护”主线程免受工作线程引起的错误(例如取消引用的空指针、被零除等)的影响。 “工作线程”是指由 pthread_create() 创建的 posix 线程。

不幸的是,我们不能使用异常——所以没有“catch”等。

这是我的测试程序(C++):

void* workerThreadFunc(void* threadId) {
  int* a = NULL;
  *a = 5; //Error (segmentation fault)
  pthread_exit(NULL);
}

int main() {
  cout << "Main thread start" << endl;

  pthread_t workerThread;
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  pthread_create(&workerThread, &attr, workerThreadFunc, (void*)0);
  pthread_join(workerThread, NULL);

  cout << "Main thread end" << endl;
}

在上面的例子中,workerThread 引起的错误会终止整个程序。但我希望主线程继续运行,尽管有这个错误。这有可能实现吗?

【问题讨论】:

  • '不幸的是,我们不能使用异常 - 所以没有“catch”等。' - 你当然可以。异常将在引发它的线程中抛出。
  • “保护”其他线程免受异常/错误影响的最佳方法是修复它们,使其不会发生或被检测和处理/记录/无论如何。
  • 注意 - 在您上面的示例中,您正在尝试加入尚未创建的线程。
  • @MartinJames 啊,是的,你是对的,谢谢。尽管将这些行复制到我的示例中只是一个简单的错误(我的真实程序以正确的顺序运行)。我会编辑我的帖子。
  • @MartinJames “当然可以。异常将在引发它的线程中抛出”。我们的代码需要针对不支持异常的系统进行编译。

标签: c++ c pthreads signals posix


【解决方案1】:

在我看来,您应该使用多个进程,而不是线程。独立进程会自动受到保护,不会在其他进程中发生此类错误。

您可以使用管道或共享内存(或其他形式的 IPC)在线程之间传递数据,这具有仅共享您打算共享的内存的额外好处,因此工作“线程”中的错误无法踩踏主“线程”的堆栈,因为它是一个具有独立地址空间的独立进程。

线程可能很有用,但也有一些缺点,有时在单独的进程中运行更合适。

【讨论】:

  • “进程”是指例如 fork()?
  • 是的,fork() 来自 master 的新子进程,或者 exec() 为工人提供不同的可执行文件,或者只是继续运行相同的可执行文件,但让子进程以“工人”模式运行。
【解决方案2】:

我能想到的唯一方法是注册一个信号处理程序,它可以取消当前正在运行的线程,而不是中止程序,如下所示:

void handler(int sig)
{
    pthread_exit(NULL);
}

signal(SIGSEGV, handler);

但是请注意,这是不安全的,因为 pthread_exit 没有被列为信号处理程序中的安全系统调用之一。它可能有效,也可能无效,具体取决于您运行的操作系统以及您正在处理的信号。

【讨论】:

  • 谢谢。这似乎可以解决问题,至少目前在一个简单的测试中是这样。我需要研究你所说的安全问题。
  • 如果在该线程堆栈上创建了一个对象并且该对象定义了析构函数,pthread_exit 似乎会导致 SIGABRT。这将导致捕获的 SIGABRT 的无限循环(如果在 sighandler 中捕获它们),或者导致程序完全崩溃。例如,如果我声明一个非常简单结构的对象,就会发生这种情况。如果该对象定义了析构函数,即使只是 ~A() {},它也会崩溃。但是如果没有定义这个析构函数,就不会发送 SIGABRT。如果我声明例如 vector. 也会出现问题
【解决方案3】:

假设您的系统以某种 POSIX 方式使用信号(尽管这可能属于“无例外”规则),那么 POSIX 会说:

在生成时,应确定该信号是为进程还是为进程内的特定线程生成的。由特定线程的某些动作(例如硬件故障)生成的信号应为导致信号生成的线程生成。

因此您可以在每个 pthread 基础上处理SIGSEGV、SIGFPE 等(但请注意,您只能为整个进程设置一个信号处理函数)。因此,您可以“保护”该进程不因单个 pthread 中的故障而停止死机……直到某一点。当然,问题是您可能会发现很难判断进程(失败的 pthread 和所有其他 pthread)处于什么状态。失败的 pthread 可能持有许多互斥锁。失败的 pthread 可能会使一些共享数据结构变得一团糟。谁知道事情的缠结是什么——除非pthreads本质上是独立的。可以安排其他 pthreads “优雅地”关闭......而不是崩溃和烧毁。最后,停止所有 pthread 死机可能会更安全,而不是尝试在一些定义不明确的状态下继续。这完全取决于应用程序的性质。

没有什么是徒劳的...线程之间的通信比进程更容易,而且启动和停止的成本更低——进程更不容易受到其他进程失败的影响。

【讨论】:

    猜你喜欢
    • 2023-03-10
    • 2017-02-24
    • 1970-01-01
    • 1970-01-01
    • 2015-09-08
    • 2021-11-11
    • 2020-05-02
    • 2017-05-28
    • 2017-09-21
    相关资源
    最近更新 更多