Подтвердить что ты не робот

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

Я создаю поток, и я помещаю его в бесконечный цикл. При проверке кода с valgrind я получаю утечки памяти. Вот мой код:

#include <pthread.h>
#include <time.h>

void thread_do(void){
    while(1){}
}

int main(){
    pthread_t th;   
    pthread_create(&th, NULL, (void *)thread_do, NULL);

    sleep(2);
    /* I want to kill thread here */
    sleep(2);
    return 0;
}

Итак, поток создается в основном и просто запускает thread_do() все время. Есть ли способ убить его изнутри через 2 секунды? Я пробовал как pthread_detach(th), так и pthread_cancel(th), но у меня все еще есть утечки.

4b9b3361

Ответ 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 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 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(&mxq,NULL);
  pthread_mutex_lock(&mxq);
  pthread_create(&th,NULL,thread_do,&mxq);

  sleep(2);

  /* unlock mxq to tell the thread to terminate, then join the thread */
  pthread_mutex_unlock(&mxq); 
  pthread_join(th,NULL);

  sleep(2);
  return 0;
}

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

Ответ 2

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

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

    A thread 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 exit status.  (Joining with a thread is the only way
    to know that cancellation has completed.)