检查程序退出后仍在运行的线程

时间:2010-08-30 05:03:02

标签: c pthreads

gcc 4.4.3 c89 pthreads

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

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

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

非常感谢任何建议,

13 个答案:

答案 0 :(得分:15)

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


如果你想编写你的程序,以便确保所有线程都在main()退出之前退出,那么你需要在{{{{}}结束时遍历所有线程。 1}},在每个人身上调用main()。 (这也意味着你不应该创建分离的线程,或者分离它们。)

答案 1 :(得分:7)

工具方法

你可以使用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 ./<program-name>

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

==2203== Thread #2 was created
==2203==    at 0x31C96D3CDE: clone (in /lib64/libc-2.5.so)
==2203==    by 0x31CA206D87: pthread_create@@GLIBC_2.2.5 (in /lib64/libpthread-2.5.so)
==2203==    by 0x4A0B206: pthread_create_WRK (hg_intercepts.c:229)
==2203==    by 0x4A0B2AD: pthread_create@* (hg_intercepts.c:256)
==2203==    by 0x40060A: main (main.c:26)
==2203== 
==2203== Thread #2: Exiting thread still holds 1 lock
==2203==    at 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==    by 0x31C96D3D1C: clone (in /lib64/libc-2.5.so)

如果你没有在线程可能退出的任何地方添加互斥锁解码代码(例如使用pthread_exit),你将会使用这种方法得到误报,但是一旦识别出这种假阳性就很容易。

另类方法(推荐)

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

如果您采用这种方法,并且该过程在您预期时不会退出,那么您就知道线程仍在运行。然后,您可以将调试器附加到进程,以确定哪些线程仍在运行以及它们正在执行的操作。

答案 2 :(得分:6)

如果您打算使用Boost。线程库,则可以使用.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;
}

答案 3 :(得分:6)

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

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

答案 4 :(得分:4)

原始答案已更新,以解决pthread_exit()方案。

假设您想要在pthread_join()返回之前判断所有线程是否main() - 正确,有以下几种方法:

  1. gdb下运行它并在main()的最后一行中断,然后查看“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
    

答案 5 :(得分:3)

如果错误,请纠正我,但在所有正在运行的线程结束之前,程序才会完成。

答案 6 :(得分:2)

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

1)将其设置为使其初始计数与线程数相同:

sem_init( &semThreadCount, 0, threadCount );

2)修改你的线程以“通知”他们正在优雅地退出:

sem_wait( &semThreadCount );

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

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

通过这种方式,您可以确保在退出时没有线程仍在运行,或者通过一些日志记录,可以在退出后知道哪些线程仍在运行。

请记住sem_destroy sempahore。

答案 7 :(得分:1)

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

请参阅pthread tutorial

答案 8 :(得分:1)

可以使用waitpid检查进程的存在,即是否仍有任何线程在运行。

如果您只是希望您的进程继续使用所有线程,但您不再需要main,那么您可以通过pthread_exit结束该线程。除了明确的exit或简单的return之外,这不会终止您的其他线程。

答案 9 :(得分:1)

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

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

答案 10 :(得分:0)

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

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

答案 11 :(得分:0)

如果你想要一个外部方法来观察你的进程执行的线程,在Linux上你可以查看/ proc /(pid)/ task。这就是像ps(1)或top(1)这样的方法工具。

请参阅http://linux.die.net/man/5/proc

答案 12 :(得分:0)

你错过了重要部分:

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

但是,在退出之前,您应该对所有线程执行pthread_join()。这可确保所有线程终止,并允许您free()所有相应的pthread_t,以便您不会泄漏内存。

如果说,valgrind可以为您提供有关您之后没有清理过的线程的全面视图。使用--leakcheck=full运行它,并确保不会留下各种结构。这些将表明有一个线程没有完全正确终止。