【问题标题】:check for threads still running after program exits检查程序退出后仍在运行的线程
【发布时间】:2010-08-30 05:03:02
【问题描述】:

gcc 4.4.3 c89 pthreads

我使用 valgrind 来检查内存错误。

我只是想知道是否有任何Linux工具可以检测程序完成后尚未终止的正在运行的线程。

我正在运行一个多线程应用程序,需要一个工具来确保所有线程都已完成。

非常感谢您的任何建议,

【问题讨论】:

  • 据我所知,您提出了两个单独的问题。一个是用于“类似调试器”的检测,如果仍有线程在运行,另一个是确保在您结束main 时所有线程都已完成。请更清楚您想要回答的问题,以及您提出赏金的问题
  • 我不太确定这样的工具会有多大帮助。如果您不打算在退出之前等待线程完成,那么您永远无法确定您的进程是否在线程完成之前完成。您可以运行该工具一次(或多次)并查看所有线程在进程退出之前完成。然后再次运行该进程,所有线程在进程退出之前都没有完成。

标签: c pthreads


【解决方案1】:

如果程序已经终止(因为初始线程从main() 返回,某个线程调用exit(),或者进程收到了致命信号),那么您可以保证所有线程都已以极端偏见终止。


如果您想编写程序以确保main() 退出之前其所有线程都已退出,那么您需要在main() 的末尾循环遍历所有线程,每一个都调用pthread_join()。 (这也意味着你不应该创建分离的线程,或者分离它们)。

【讨论】:

  • 但是子线程被终止(带有极端偏见)可能是个问题。因此,作为 main() 的最后一部分,您想尝试让子线程退出(并等待它们退出)。因此问题;有没有办法检查这不会发生。 exit() 和 signal() 是一个单独且更难解决的案例。
  • @Martin York:OP 说他想要“一个确定...的工具” 听起来他想要一个外部程序来检查他的程序是否已经退出.
  • @caf:没有“需要”循环所有线程:从主线程调用pthread_exit 将阻止进程终止,直到所有线程都退出。此外,从主线程调用pthread_exit 比为每个线程使用pthread_join 要容易得多。如果您也有分离的线程,它也可以工作!我认为这是一个更好的解决方案。
  • @Dan,如果您想开始删除其他线程所依赖的共享资源,则不要这样做。那你需要先加入,再清理。
  • 请注意,如果实现在内部为SIGEV_THREAD 计时器或其他幕后事物创建了任何线程,则从所有线程调用pthread_exit 不足以终止进程。可以说这样的实现(glibc)是有问题的,但是由于标准声明SIGEV_THREAD 回调线程的生命周期不能由应用程序确定,它并不比标准允许的差多少。使用exit 或从main 返回可能更安全。
【解决方案2】:

一种工具方法

您可以使用 Valgrind 来帮助解决这个问题(通过它的 Helgrind 工具),但它需要对代码进行少量修改。对于每个线程,在创建线程时使线程锁定一个唯一的互斥锁,并在线程退出时释放互斥锁。然后,当在 Helgrind 下运行时,如果程序终止时线程尚未退出,您将收到警告,因为线程仍将锁定互斥锁。考虑这个示例线程启动例程:

void * thread_start (void *arg)
{
    pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

    pthread_mutex_lock(&mutex);

    // ...
    // Here the thread does whatever it normally does
    // ...

    // Unlock the mutex before exiting
    pthread_mutex_unlock(&mutex);
}

只需使用 Valgrind 的 Helgrind 工具运行程序,如下所示:

$ valgrind --tool=helgrind ./

如果程序终止时线程没有退出,那么 Helgrind 会产生如下警告:

==2203== 线程 #2 已创建 ==2203== 在 0x31C96D3CDE:克隆(在 /lib64/libc-2.5.so 中) ==2203== 0x31CA206D87:pthread_create@@GLIBC_2.2.5(在 /lib64/libpthread-2.5.so 中) ==2203== 由 0x4A0B206:pthread_create_WRK (hg_intercepts.c:229) ==2203== 由 0x4A0B2AD: pthread_create@* (hg_intercepts.c:256) ==2203== by 0x40060A: main (main.c:26) ==2203== ==2203== 线程 #2:退出线程仍然持有 1 个锁 ==2203== 在 0x4005DD:thread_start (main.c:13) ==2203== by 0x4A0B330: mythread_wrapper (hg_intercepts.c:201) ==2203== by 0x31CA20673C: start_thread (in /lib64/libpthread-2.5.so) ==2203== 由 0x31C96D3D1C:克隆(在 /lib64/libc-2.5.so 中)

如果您没有在线程可能退出的任何地方添加互斥锁解锁代码(例如,使用pthread_exit),使用此方法会出现误报,但一旦识别出此类误报,就很容易修复。

另一种方法(推荐)

说了以上所有,这可能不是我自己会采取的方法。相反,我会编写程序,使其无法在所有线程退出之前终止。实现这一点的最简单方法是在从main 返回之前从主线程调用pthread_exit。这样做意味着只要任何其他线程仍在运行,该进程就会保持活动状态。

如果您采用这种方法,并且进程没有在您期望的时候退出,那么您就知道线程仍在运行。然后,您可以将调试器附加到进程以确定哪些线程仍在运行以及它们在做什么。

【讨论】:

    【解决方案3】:

    如果你打算使用Boost.Threads 库,那么你可以使用.join() 方法。

    For example:

    #include <boost/thread/thread.hpp>
    #include <iostream>
    void hello()
    {
      std::cout <<
        "Hello world, I'm a thread!"
        << std::endl;
    }
    
    int main(int argc, char* argv[])
    {
      boost::thread thrd(&hello);
      thrd.join();
      return 0;
    }
    

    【讨论】:

    【解决方案4】:

    在这个类似的问题中有一个简单的技巧:Multiple threads in C program

    如果您从 main 调用 pthread_exit,您的进程将在所有其他线程完成之前不会退出。

    【讨论】:

      【解决方案5】:

      原始答案已更新为解决 pthread_exit() 场景。

      假设您想判断所有线程是否在您从main() 返回之前正确地编辑了pthread_join(),有几种方法:

      1. gdb下运行,在main()的最后一行break,然后查看“threads”命令的输出。应该只有主线程。

      2. 创建一个覆盖pthread_create 的共享库,并使用一个包装器来记录启动的线程数。线程包装器递增一个计数器并调用实际的线程函数,而使用pthread_create_key() 注册的函数将在线程返回或退出时递减它。库析构函数将检查计数器是否为零,这意味着它们都已终止。通过LD_PRELOAD=checker.so ./your_executable 将它与您的可执行文件一起使用(无需修改代码)。

        在 Debian 5.0.5 上测试。

        checker.c

        #define _GNU_SOURCE
        #include <pthread.h>
        #include <stdio.h>
        #include <dlfcn.h>
        #include <stdlib.h>
        
        /* thread-local storage key */
        static pthread_key_t tls_key = 0;
        static int counter = 0;
        static pthread_mutex_t g_mutex;
        
        /* TLS destructor prototype */
        void on_thread_end(void*);
        
        void __attribute__ ((constructor))
        init_checker()
        {
            pthread_mutexattr_t attr;
            pthread_mutexattr_init(&attr);
            pthread_mutex_init(&g_mutex, &attr);
            pthread_mutexattr_destroy(&attr);
            pthread_key_create(&tls_key, &on_thread_end);
        }
        
        void __attribute__ ((destructor))
        finalize_checker()
        {
            int remain;
            pthread_mutex_lock(&g_mutex);
            remain = counter;
            pthread_mutex_unlock(&g_mutex);
            pthread_mutex_destroy(&g_mutex);
            if (remain)
                fprintf(stderr, "Warning: %d threads not terminated\n", remain);
            pthread_key_delete(tls_key);
        }
        
        /* thread function signature */
        typedef void* (*ThreadFn)(void*);
        
        struct wrapper_arg
        {
            ThreadFn fn;
            void* arg;
        };
        
        /* TLS destructor: called for every thread we created
           when it exits */
        void
        on_thread_end(void *arg)
        {
            free(arg);
            pthread_mutex_lock(&g_mutex);
            --counter;
            pthread_mutex_unlock(&g_mutex);
        }
        
        static void*
        thread_wrapper(void *arg)
        {
            void *ret;
            struct wrapper_arg *warg;
        
            warg = (struct wrapper_arg*)arg;
        
            /* Thread started, increment count. */
            pthread_mutex_lock(&g_mutex);
            ++counter;
            pthread_mutex_unlock(&g_mutex);
        
            /* set thread-specific data to avoid leaks
             * when thread exits
             */
            pthread_setspecific(tls_key, arg);
        
            /* Run the actual function. */
            ret = (*warg->fn)(warg->arg);
        
            /* Thread finishes, TLS destructor will be called. */
        
            return ret;
        }
        
        /* pthread_create signature */
        typedef int (*CreateFn)(pthread_t*,const pthread_attr_t*,ThreadFn,void*);
        
        /* Overriding phtread_create */
        int
        pthread_create(
            pthread_t *thread,
            const pthread_attr_t *attr,
            ThreadFn start_routine,
            void *arg)
        {
            CreateFn libc_pthread_create;
            struct wrapper_arg *warg;
        
            /* Get a handle to the real function. */
            libc_pthread_create
                = (CreateFn)dlsym(RTLD_NEXT, "pthread_create");
            if (!libc_pthread_create)
                return -1;
        
            /* Wrap user function. */
            warg = malloc(sizeof(struct wrapper_arg));
            if (!warg)
                return -1;
            warg->fn = start_routine;
            warg->arg = arg;
        
            /* Create a thread with a wrapper. */
            return libc_pthread_create(thread, attr, &thread_wrapper, warg);
        }
        

        生成文件

        CFLAGS+=-fpic -O3
        checker.so: checker.o
            gcc -shared -Wl,-soname,$@ -o $@ $^ -ldl -lpthread
        

      【讨论】:

      • 这并不完全有效,除非您还提供一种方法来从线程退出而不从线程启动例程返回的位置减少线程计数(例如,如果线程调用 pthread_exit 怎么办?)
      • @Dan,嗯,你是对的,尽管这不是首选方法,但存在技术上的可能性。我怀疑应该有一个基于 TLS 的解决方案......
      • @Alex-B 我喜欢这种方法 (+1)。但我想知道是否有任何方法可以通过静态链接实现类似的方法?
      • @msalvadores 如果您静态链接,除了 DSO 构造函数/析构函数位之外,这会以类似的方式工作。您将不得不手动初始化/销毁。但是,嘿,如果您正在修改构建过程,不妨在代码中添加两行。
      【解决方案6】:

      如果有错误,请纠正我,但是直到所有正在运行的线程都结束后,程序才完成。

      【讨论】:

      • 不正确。没有记录(我认为) pthreads 如何终止。但是在我使用的那些中,如果主线程(由操作系统创建)退出,那么所有线程都会自动终止。这就是为什么在让 main() 退出之前应该等待所有子线程完成的原因。这是什么问题。
      • @Martin,这是正确的。我只是想确保在主要退出之前一切都已清理干净。 Valgrind 用于内存泄漏,我正在寻找一个可以检查所有线程是否已成功终止的工具。谢谢。
      • @robUK 你应该用这个更具体的信息更新你的问题。
      【解决方案7】:

      您不需要任何外部工具:我会使用简单的 semaphore 来跟踪线程。

      1) 设置它,使其初始计数与您的线程数相同:

      sem_init( &semThreadCount, 0, threadCount );
      

      2) 修改您的线程以“通知”它们正在正常退出:

      sem_wait( &semThreadCount );
      

      3) 您可以在线程完成或信号量为 0 时退出,或者只打印剩余的信号量值并退出,这将是仍在运行的线程数:

      int v;
      sem_getvalue( &semThreadCount, &v );
      

      这样,您既可以确保退出时没有线程仍在运行,也可以通过一些日志记录,在您退出后能够知道哪些线程仍在运行。

      请记住sem_destroy sempahore。

      【讨论】:

        【解决方案8】:

        如果您不能使用 C++ 并因此无法使用 KMan 的答案,那么您也可以使用“C”API 加入分离的 pthread。 (加入意味着等待分离的线程完成它们的工作。)

        请参阅pthread tutorial

        【讨论】:

          【解决方案9】:

          可以通过waitpid查看进程是否存在,即是否还有线程在运行。

          如果您只想让您的进程继续处理所有线程,但您不再需要main 之一,您可以通过pthread_exit 结束该线程。除了显式的exit 或简单的return 之外,这不会终止您的其他线程。

          【讨论】:

            【解决方案10】:

            这样的工具已经存在。在 Linux 上,您可以使用 pstop。在 Windows 上,好的 ole 任务管理器可以完成这项工作:。只需检查您的进程是否仍然存在:

            • 如果进程仍然存在,则表示其中的一个或多个线程正在运行。
            • 如果没有更多线程在运行,则终止进程。

            【讨论】:

              【解决方案11】:

              如果它们是线程(而不是进程),那么您只需要检查您的进程是否正在运行,因为线程在进程内运行。

              您可以使用 ps -ef 检查进程是否正在运行,然后将结果通过管道传输到 grep 以查找您的特定进程。

              【讨论】:

              • 可能他想确保所有创建的线程在主线程退出之前都已经退出。可能他想在所有线程中进行一些清理并将一些内容写入数据库/文件以进行新启动。无论如何,这只是一个猜测。
              • @Praveen,这是正确的。我只想确保所有线程在主退出之前成功完成。谢谢。
              【解决方案12】:

              如果您想通过外部方式观察您的进程正在执行的线程,在 Linux 上,您可以查看 /proc/(pid)/task。这就是 ps(1) 或 top(1) 等工具使用的方法。

              http://linux.die.net/man/5/proc

              【讨论】:

                【解决方案13】:

                你错过了重要的部分:

                除非所有线程都终止,否则程序无法退出

                但是,您应该在退出之前对所有线程进行pthread_join()。这确保了所有线程都终止了,并允许您free()它们各自的pthread_ts,这样您就不会从它们那里泄漏内存。

                话虽如此,valgrind 可以让您全面了解尚未清理的线程。使用--leakcheck=full 运行它,并确保你没有留下各种结构。这些将表明您没有完全正确终止的线程。

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-04-10
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-04-27
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多