Как я могу уничтожить pthread, который находится в бесконечном цикле, из-за пределов этого цикла?

#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:

Несколько небольших мыслей:

  1. Вы пытаетесь отменить свой поток, но если действующая политика отмены предназначена для отложенной отмены, ваш 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).
 
  1. Вы не присоединяетесь к потоку в своем простом примере кода; вызовите 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.)