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