Проверить, что потоки все еще запущены после выхода программы

gcc 4.4.3 c89 pthreads

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

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

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

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

Ответ 1

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


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

Ответ 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 ./<program-name>

Если поток не завершился, когда программа завершилась, тогда Helgrind выдает предупреждение, подобное этому:

==2203== Thread #2 was created
==2203==    at 0x31C96D3CDE: clone (in /lib64/libc-2.5.so)
==2203==    by 0x31CA206D87: [email protected]@GLIBC_2.2.5 (in /lib64/libpthread-2.5.so)
==2203==    by 0x4A0B206: pthread_create_WRK (hg_intercepts.c:229)
==2203==    by 0x4A0B2AD: [email protected]* (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. Это будет означать, что процесс останется в живых, пока какой-либо другой поток все еще работает.

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

Ответ 3

Если вы планируете использовать Boost.Threads library, то вы можете использовать метод .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

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

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

Ответ 5

Оригинальный ответ был обновлен, чтобы обратиться к сценарию pthread_exit().

Предполагая, что вы хотите определить, были ли все потоки pthread_join() правильными, прежде чем вы вернетесь из main(), существует несколько способов:

  • Запустите его под gdb и перерыв в последней строке main(), а затем посмотрите на вывод команды "threads". Там должен быть только основной поток.

  • Создайте общую библиотеку, которая переопределяет 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,[email protected] -o [email protected] $^ -ldl -lpthread
    

Ответ 6

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

Ответ 7

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

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

sem_init( &semThreadCount, 0, threadCount );

2) Измените свои потоки, чтобы "уведомить", что они изящно уходят:

sem_wait( &semThreadCount );

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

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

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

Помните sem_destroy и sempahore.

Ответ 8

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

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

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

Если это потоки (а не процессы), вам просто нужно проверить, работает ли ваш stll-процесс, потому что потоки выполняются внутри процесса.

Вы можете проверить, запущен ли процесс с ps -ef, а затем передать результат в grep, чтобы найти ваш конкретный процесс.

Ответ 12

Если вы хотите, чтобы внешние средства отслеживали потоки в процессе выполнения вашего процесса, в Linux вы можете посмотреть в /proc/ (pid)/task. То, что используют инструменты метода, такие как ps (1) или top (1).

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

Ответ 13

Вам не хватает важной части:

Программа не может выйти, если все ее потоки не завершены.

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

Сказали, что valgrind может дать вам полное представление о потоках, которые вы не очистили после. Запустите его с помощью --leakcheck=full и убедитесь, что вы не оставляете позади вас разные структуры. Они укажут, что есть поток, который вы полностью не завершили.