【问题标题】:Handling process exit from within a thread in a multi-threaded application处理多线程应用程序中线程内的进程退出
【发布时间】:2011-10-10 07:26:16
【问题描述】:

我有一个多线程应用程序。该应用程序具有以下线程集:

  1. 休眠的主线程。所有信号都在这个线程中被阻塞。

  2. 执行所有处理的线程 t1。所有信号都在这个线程中被阻塞。

  3. 由我使用的第三方组件设置的信号处理线程 (t2)。该线程仅等待 SIGINT 和 SIGKILL 信号。所有其他信号都在此线程中被阻止。

  4. 我自己的自定义信号处理线程 (t3)。

现在,为了处理进程退出,我向我的进程发送了一个 SIGUSR1。该信号将被线程 t3 捕获。线程 t3 将调用清理例程并退出。这里的问题是线程 t3 试图清理其他线程访问的资源。这将导致间歇性崩溃。

显然,我目前的解决方案没有优雅的进程退出处理。我的问题是在这种情况下应该如何处理进程退出?信号处理线程应该如何停止剩余线程然后退出进程?

或者有比发送信号 (SIGUSR1) 终止进程更好的方法吗?

我的应用程序是用 C 语言编写并在 RHEL 5 上运行的。

【问题讨论】:

  • 为什么你的第三个线程不能告诉另一个线程他们必须停止?然后你只需要等待每个线程停止,并在它们终止后清理资源。这看起来更像是资源共享问题而不是信号/退出问题
  • “t2”(第三方组件线程)收到INTTERM时会做什么?它会终止整个进程(如exit()),还是仅终止自身(pthread_exit())还是什么?

标签: c linux multithreading


【解决方案1】:

在清理例程上放置一个互斥锁,这样两个线程就不会尝试同时清理。想要关闭的线程应该获取互斥锁,然后告诉其他线程关闭(无论您通常采用哪种方式)。这样,只有一个线程应该进行实际的清理。

void cleanup()
{
    pthread_mutex_lock(m);
    if (!cleanup_done) {
        cleanup_done = 1;
        tell_other_threads_to_stop();
        wait_for_other_threads_to_finish();
        clean_up_common_resources();
    }
    pthread_mutex_unlock(m);
}

或者,您可以永久锁定所有共享资源,清理它们并在持有锁的同时终止整个进程。

【讨论】:

  • 线程应该只清理它们自己的资源,所以不需要序列化清理。序列化清理可能会治愈一种症状,但不能治愈线程竞相破坏同一对象的根本问题。
  • 您好 mpartel/Maxim,这里的问题不是 2 个线程试图清理进程。问题是我只需要清理 1 个线程,即我的信号处理线程。
【解决方案2】:

在多线程应用程序中处理信号的一种好方法是只有一个线程等待信号,所有其他线程都应该阻塞信号。通常,这是初始化所有组件/库、创建其他线程、等待信号并以有序方式终止应用程序的主线程。

不清楚为什么您的应用程序有线程 1 和 4。线程 2 可以完成所有工作并处理所有信号(应该是主线程)。一般来说,让第三方组件处理信号不是一个好主意,因此最好在线程 3 中阻塞信号。

应用程序应处理的标准终止信号是SIGINT(在ctrl-c 击键时发送)和SIGTERMkill <app-pid> 发送。

【讨论】:

  • 嗨,Maxim,我知道只有 1 个线程处理所有信号总是更好。但是,我使用的第三方组件打破了这个不成文的规则。因为我不能不使用这个组件,所以我需要忍受他们的信号处理线程。
  • 您可以在第三方线程中阻塞信号。见pthread_sigmask
  • 我不能这样做 Maxim,因为第三方工具会在 SIGINT 和 SIGKILL 的情况下进行自己的特定清理。而且由于它是第三方工具,我无法真正影响他们做出改变。
  • 顺便说一句,SIGKILL 至少在 Linux 上不能被捕获、阻止或忽略。其次,该第 3 方库是否有一个取消初始化函数,您可以调用它来干净地终止它?无论如何,您可以阻止第 3 方中的信号,处理线程中的信号,并在终止时解除阻止 lib 中的信号并向其发送 SIGINT
  • Maxim,我想我需要做的是从我的信号处理线程中停止(取消或终止)所有其他线程。一旦所有其他线程都消失了,我可以安全地从信号处理线程调用退出。这对马克西姆有用吗?
【解决方案3】:

您的 t3 必须取消 t1/t2 并在调用 exit() 之前等待它们的终止,因为这里存在竞争条件。

在这种情况下不要偷懒,因为没有其他办法。

顺便说一句,由于你的 main() 什么都不做,你可以简单地使用显式 pthread_exit() 提前完成它。

【讨论】:

    猜你喜欢
    • 2019-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多