проверять потоки, работающие после выхода из программы - PullRequest
10 голосов
/ 30 августа 2010

gcc 4.4.3 c89 pthreads

Я использую valgrind для проверки ошибок памяти.

Мне просто интересно, есть ли какой-нибудь инструмент для linux, который может обнаруживать запущенные потоки, которые не были прекращены после завершения программы.

Я запускаю многопоточное приложение и мне нужен инструмент, чтобы убедиться, что все потоки завершены.

Большое спасибо за любые предложения,

Ответы [ 13 ]

15 голосов
/ 30 августа 2010

Если программа завершилась (поскольку исходный поток возвратился из main(), какой-то поток с именем exit() или фатальный сигнал был получен процессом), тогда вы гарантированно завершили все потоки с предубеждением.


Если вы хотите написать свою программу так, чтобы она гарантировала , что все ее потоки вышли до выхода main(), тогда вам нужно перебрать все ваши потоки в концеmain(), вызывая pthread_join() на каждом.(Это также означает, что вы не должны создавать свои потоки отделенными или отделять их).

8 голосов
/ 10 сентября 2010

Подход к инструменту

Вы можете использовать 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);
}

Просто запустите программу, используя инструмент Helgrind от Valgrind, примерно так:

$ 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.Это будет означать, что процесс останется живым, пока любой другой поток все еще работает.

Если вы воспользуетесь этим подходом, и процесс не завершится, когда вы этого ожидаете, то вы знаете, чтопоток все еще работает.Затем вы можете подключить к процессу отладчик, чтобы определить, какие потоки все еще работают и что они делают.

6 голосов
/ 08 сентября 2010

В этом похожем вопросе есть простой трюк: Несколько потоков в программе на C

Если вы вызовете pthread_exit из main, ваш процесс не завершится, пока все остальные потоки не завершатся.

6 голосов
/ 30 августа 2010

Если вы планируете использовать библиотеку Boost .Threads, вы можете использовать метод .join().

Например:

#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 голосов
/ 09 сентября 2010

Исходный ответ был обновлен до сценария с адресом 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);
    }
    

    Makefile

    CFLAGS+=-fpic -O3
    checker.so: checker.o
        gcc -shared -Wl,-soname,$@ -o $@ $^ -ldl -lpthread
    
3 голосов
/ 30 августа 2010

Исправьте меня, если ошиблись, но программа не будет завершена, пока не завершатся все запущенные потоки.

2 голосов
/ 12 сентября 2010

Для этого вам не нужен внешний инструмент: я бы отслеживал темы, используя вместо этого простой семафор .

1) установите его так, чтобы его начальное количество совпадало с числом ваших потоков:

sem_init( &semThreadCount, 0, threadCount );

2) Измените ваши темы, чтобы "уведомить", что они завершаются изящно:

sem_wait( &semThreadCount );

3) Вы можете либо выйти, когда завершатся потоки, либо когда семафор равен 0, либо просто распечатать оставшееся значение семафора и выйти, что будет числом все еще работающих потоков:

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

Таким образом, вы можете убедиться, что ни один из потоков еще не запущен, если вы выходите, или, с некоторыми регистрациями, узнать, какие из них все еще работают после вашего выхода.

Не забывайте также sem_destroy о семпахоре.

1 голос
/ 11 сентября 2010

Такие инструменты уже существуют.В Linux вы можете использовать ps или top.В Windows хороший менеджер задач ole делает свою работу:Просто проверьте, существует ли еще ваш процесс:

  • , если процесс все еще существует, это означает, что один или несколько потоков в нем работают.процесс прекращен.
1 голос
/ 08 сентября 2010

Существование процесса, то есть если какой-либо поток еще работает, можно проверить с помощью waitpid.

Если вы просто хотите, чтобы ваш процесс продолжался со всеми потоками, но вывам больше не нужен main, вы можете завершить этот поток с помощью pthread_exit.За исключением явного exit или простого return, это не прервет ваши другие потоки.

1 голос
/ 07 сентября 2010

Если вы не можете использовать C ++ и, следовательно, ответ KMan, вы также можете присоединить отдельные потоки с помощью API-интерфейса "C".(Присоединение означает ожидание окончания работы отсоединенных потоков.)

См. Учебник pthread .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...