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

Я создаю поток и помещаю его в бесконечный цикл.Утечки памяти возникают при проверке кода с помощью 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;
}

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

Ответы [ 2 ]

35 голосов
/ 01 ноября 2011

Как указал @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(&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() после остановки потока.Если поток отсоединен, вам не нужно присоединяться к нему, но вы не будете точно знать, когда он закончится (или даже приблизительно, если вы не добавите другой способ указать его выход).

5 голосов
/ 01 ноября 2011

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

  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).
Вы не присоединяетесь к потоку в своем простом примере кода;позвоните 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.)
...