#c #memory-leaks #pthreads #threadpool
#c #утечки памяти #pthreads #threadpool
Вопрос:
Я создаю поток и помещаю его в бесконечный цикл. Я получаю утечки памяти при проверке кода с помощью valgrind. Вот мой код:
#include <pthread.h>
#include <time.h>
void thread_do(void){
while(1){}
}
int main(){
pthread_t th;
pthread_create(amp;th, NULL, (void *)thread_do, NULL);
sleep(2);
/* I want to kill thread here */
sleep(2);
return 0;
}
Таким образом, поток создается в main и просто постоянно запускает thread_do() . Есть ли способ убить его изнутри main через 2 секунды? Я пробовал и pthread_detach(th)
то, и pthread_cancel(th)
другое, но все равно получаю утечки.
Комментарии:
1. Отмена pthreads означает, что поток не имеет возможности очистить любую выделенную память.
2. Хорошо, я решил это, просто имея глобальный
keepalive=1
. Затем я изменил значениеwhile(1)
наwhile(keepalive)
. Чтобы убить поток, теперь мне просто нужно изменить значение переменной keepalive на 0 и вуаля!
Ответ №1:
Как указал @sarnold, по умолчанию ваш поток не может быть отменен pthread_cancel()
без вызова каких-либо функций, которые являются точками отмены… но это можно изменить, используя pthread_setcanceltype()
для установки типа отмены потока значение асинхронный вместо отложенный. Чтобы сделать это, вы должны добавить что-то вроде pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL);
рядом с началом вашей потоковой функции, прежде чем запускать цикл. Затем вы сможете завершить поток , вызвав pthread_cancel(th)
его из main()
.
Обратите внимание, однако, что отмена потоков таким образом (независимо от того, асинхронный он или нет) не приводит к очистке каких-либо ресурсов, выделенных в функции потока (как отметил Кевин в комментарии). Чтобы сделать это чисто, вы можете:
- Убедитесь, что поток не делает ничего, что ему нужно очистить перед выходом (например, используя
malloc()
для выделения буфера) - Убедитесь, что у вас есть какой-то способ очистки после потока в другом месте, после завершения потока
- Используйте
pthread_cleanup_push()
иpthread_cleanup_pop()
для добавления обработчиков очистки для очистки ресурсов при отмене потока. Обратите внимание, что это все еще рискованно, если тип отмены асинхронный, потому что поток может быть отменен между выделением ресурса и добавлением обработчика очистки. - Избегайте использования
pthread_cancel()
и попросите поток проверить некоторое условие, чтобы определить, когда завершать (что будет проверяться в длительных циклах). Поскольку ваш поток затем проверяет само завершение, он может выполнить любую необходимую очистку после проверки.
Один из способов реализации последнего варианта — использовать мьютекс в качестве флага и протестировать его с pthread_mutex_trylock()
помощью функции, обернутой в функцию, для использования в циклических тестах:
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
/* Returns 1 (true) if the mutex is unlocked, which is the
* thread's signal to terminate.
*/
int needQuit(pthread_mutex_t *mtx)
{
switch(pthread_mutex_trylock(mtx)) {
case 0: /* if we got the lock, unlock and return 1 (true) */
pthread_mutex_unlock(mtx);
return 1;
case EBUSY: /* return 0 (false) if the mutex was locked */
return 0;
}
return 1;
}
/* Thread function, containing a loop that's infinite except that it checks for
* termination with needQuit()
*/
void *thread_do(void *arg)
{
pthread_mutex_t *mx = arg;
while( !needQuit(mx) ) {}
return NULL;
}
int main(int argc, char *argv[])
{
pthread_t th;
pthread_mutex_t mxq; /* mutex used as quit flag */
/* init and lock the mutex before creating the thread. As long as the
mutex stays locked, the thread should keep running. A pointer to the
mutex is passed as the argument to the thread function. */
pthread_mutex_init(amp;mxq,NULL);
pthread_mutex_lock(amp;mxq);
pthread_create(amp;th,NULL,thread_do,amp;mxq);
sleep(2);
/* unlock mxq to tell the thread to terminate, then join the thread */
pthread_mutex_unlock(amp;mxq);
pthread_join(th,NULL);
sleep(2);
return 0;
}
Если поток не отсоединен (обычно это не так по умолчанию), вы должны вызвать pthread_join()
после остановки потока. Если поток отсоединен, вам не нужно присоединяться к нему, но вы не будете точно знать, когда он завершится (или даже приблизительно, если вы не добавите другой способ указать его выход).
Комментарии:
1. чтобы использовать это решение мьютекса для нескольких pthread, мне понадобится один мьютекс на поток, верно? вы бы порекомендовали это решение в этом случае?
2. @RicardoCrudo Нет, только один мьютекс … если
needQuit()
он успешно блокируется, он сразу же разблокирует его…main()
удерживает мьютекс только в течение длительного периода времени. Любые потоки, используемыеneedQuit()
для проверки того, следует ли завершать работу, завершатся, как только мьютекс больше не будет заблокирован, и они заблокируют его сами только на (очень, очень короткий) момент. Только одинneedQuit()
вызов будет успешным одновременно, но каждый поток все равно получит успешный вызов один за другим.3. должна ли работать асинхронная отмена при ожидании
pthread_cond_wait(amp;c, amp;mtx);
в threadFunc, я безуспешно пытался
Ответ №2:
Несколько небольших мыслей:
- Вы пытаетесь отменить свой поток, но если действующая политика отмены предназначена для отложенной отмены, ваш
thread_do()
никогда не будет отменен, потому что он никогда не вызывает никаких функций, которые являются точками отмены:
A thread's cancellation type, determined by
pthread_setcanceltype(3), may be either asynchronous or
deferred (the default for new threads). Asynchronous
cancelability means that the thread can be canceled at any
time (usually immediately, but the system does not guarantee
this). Deferred cancelability means that cancellation will
be delayed until the thread next calls a function that is a
cancellation point. A list of functions that are or may be
cancellation points is provided in pthreads(7).
- Вы не присоединяетесь к потоку в своем простом примере кода; вызовите
pthread_join(3)
перед завершением вашей программы:
After a canceled thread has terminated, a join with that
thread using pthread_join(3) obtains PTHREAD_CANCELED as the
thread's exit status. (Joining with a thread is the only way
to know that cancellation has completed.)