Comment puis-je tuer un pthread qui est dans une boucle infinie, de l'extérieur de cette boucle?

Je crée un thread et je le mets dans une boucle infinie. Je reçois des fuites de mémoire lors de la vérification du code avec valgrind. Voici mon code:

#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;
}

Donc, un thread est créé dans main et exécute simplement thread_do() tout le temps. Est-il un moyen de le tuer de l'intérieur de main après 2 secondes? J'ai essayé à la fois pthread_detach(th) et pthread_cancel(th) mais j'ai toujours des fuites.

22
demandé sur Pithikos 2011-11-01 03:41:35

2 réponses

Comme @ sarnold l'a souligné, par défaut, votre thread ne peut pas être annulé avec pthread_cancel() sans appeler de fonctions qui sont des points d'annulation... mais cela peut être changé en utilisant pthread_setcanceltype() pour définir le type d'annulation du thread sur asynchrone au lieu de différé. Pour ce faire, vous devez ajouter quelque chose comme pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); près du début de votre fonction de thread, avant de commencer la boucle. Vous pourrez alors terminer le thread en appelant pthread_cancel(th) à partir de main().

Notez cependant que l'annulation les threads de cette façon (asynchrones ou non) ne nettoient aucune ressource allouée dans la fonction thread (comme indiqué par Kevin dans un commentaire). Pour ce faire proprement, vous pouvez:

  • assurez - vous que le thread ne fait rien qu'il doit nettoyer avant la sortie (par exemple en utilisant malloc() pour allouer un tampon)
  • assurez-vous que vous avez un moyen de nettoyer après le thread ailleurs, après la sortie du thread
  • utilisez pthread_cleanup_push() et pthread_cleanup_pop() pour ajouter des gestionnaires de nettoyage à nettoyer ressources lorsque le thread est annulé. Notez que cela reste risqué si le type d'annulation est asynchrone, car le thread peut être annulé Entre l'allocation d'une ressource et l'ajout du gestionnaire de nettoyage.
  • évitez d'utiliser pthread_cancel() et demandez au thread de vérifier une condition pour déterminer quand se terminer (ce qui serait vérifié dans les boucles de longue durée). Puisque votre thread vérifie alors la terminaison elle-même, il peut faire tout le nettoyage dont il a besoin après la vérification.

Un la façon d'implémenter la dernière option consiste à utiliser un mutex comme drapeau, et à le tester avec pthread_mutex_trylock() enveloppé dans une fonction à utiliser dans les tests de boucle:

#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;
}

Si le thread n'est pas détaché (il ne l'est généralement pas par défaut), vous devez appeler pthread_join() après avoir arrêté le thread. Si le thread est détaché, vous n'avez pas besoin de le joindre, mais vous ne saurez pas exactement quand il se termine (ou même approximativement, sauf si vous ajoutez une autre façon d'indiquer sa sortie).

34
répondu Dmitri 2011-11-01 06:50:35

Quelques petites pensées:

  1. vous essayez d'annuler votre thread, mais si la Politique d'Annulation en place concerne une annulation différée, votre thread_do() ne sera jamais annulée, car elle n'appelle jamais de fonctions qui sont des points d'annulation:
    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. vous ne Rejoignez pas le thread dans votre exemple de code simple; appelez pthread_join(3) avant la fin de votre programme:
    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.)
5
répondu sarnold 2018-09-07 01:05:28