2010-02-23 7 views
2

Je dois tirer d'un tas de fils et je voudrais les abattre gracieusement.terminaison de fil gracieuse avec pthread_cond_signal prouvant problématique

J'essaie d'utiliser pthread_cond_signal/pthread_cond_wait pour y parvenir mais je rencontre un problème.

Voici mon code. premièrement le thread_main

static void *thrmain(void * arg) 
{ 
    // acquire references to the cond var, mutex, finished flag and 
    // message queue 
    ..... 

    while(true) 
    { 
     pthread_mutex_lock(&lock); 

     if (msq.empty()) 
     { 
      // no messages so wait for one. 
      pthread_cond_wait(&cnd, &lock); 
     } 

     // are we finished. 
     if (finished) 
     { 
      // finished so unlock the mutex and get out of here 
      pthread_mutex_unlock(&lock); 
      break; 
     } 

     if (!msg.empty()) 
     { 
      // retrieve msg 
      .... 

      // finished with lock 
      pthread_mutex_unlock(&lock); 

      // perform action based on msg 
      // outside of lock to avoid deadlock 
     } 
     else 
     { 
      // nothing to do so we're 
      // finished with the lock. 
      pthread_mutex_unlock(&lock); 
     } 
    } 

    return 0; 
} 

Maintenant, tout cela semble bien et dandy (pour moi quand même).

Donc, pour arracher les fils que j'ai cette méthode

void teardown() 
{ 
    // set the global finished var 
    pthread_mutex_lock(&lock); 
    finished = true; 
    pthread_mutex_unlock(&lock); 

    // loop over the threads, signalling them 
    for (int i = 0 ; i < threads.size() ; ++i) 
    { 
     // send a signal per thread to wake it up 
     // and get it to check it's finished flag 
     pthread_cond_signal(&cnd); 
    } 

    // need to loop over the threads and join them. 
    for (int i = 0 ; i < threads.size() ; ++i) 
    { 
     pthread_join(threads[ i ].tid, NULL); 
    } 
} 

Maintenant, je sais que pthread_cond_signal ne garantit pas quel fil il se réveille signaler que je ne peux pas et rejoindre dans la même boucle. Cependant, c'est là que tout va mal. pthread_cond_signal ne fait rien s'il n'y a pas de thread en attente donc potentiellement certains des threads n'auront pas été signalés et ne sauront donc pas quitter.

Comment puis-je survivre à cela.

M.

***** MISE À JOUR ******* S'il vous plaît ne pas poster que je devrais utiliser pthread_cond_broadcast car cela présente le même comportement. il ne fera que réveiller un fil qui attend réellement sur la var. Tout thread qui est le traitement pendant cette période et revient à attendre plus tard aura manqué le signal et sera inconscient


Répondre

6

Tout d'abord, vous devez changer votre prédicats de

if (msq.empty()) { 
    // no messages so wait for one. 
    pthread_cond_wait(&cnd, &lock); 
} 

à

while (msq.empty()) { 
    // no messages so wait for one. 
    pthread_cond_wait(&cnd, &lock); 
} 

C'est une chose pthreads, vous devez vous prémunir contre spurious wakeups.

Maintenant vous pouvez changer cela à

while (msq.empty() && !finished) { 
    // no messages so wait for one. 
    pthread_cond_wait(&cnd, &lock); 
} 

Depuis après vérification, vous testez déjà si fini est réglé et quitte si oui, tout ce que vous avez à faire est de signaler tous les fils.

Ainsi, dans votre fonction de désassemblage, remplacez la boucle avec:

pthread_cond_broadcast(&cond); 

Cela devrait assurer que tous les fils se réveillent, et verra finished ensemble à true et la sortie.

Ceci est sûr même si vos threads ne sont pas bloqués dans pthread_cond_wait. Si les threads traitent un message, ils n'obtiendront pas le signal de réveil, cependant ils finiront ce traitement, entreront à nouveau dans la boucle et verront cela finished == false et sortiront.

Un autre schéma courant consiste à injecter un message de poison. Un message empoisonné est simplement un message spécial que votre thread pourrait reconnaître qui signifierait "STOP", vous placeriez autant de ces messages dans votre file d'attente que vous avez de threads.

+0

Non. Il s'agit simplement d'une répétition plus élégante de la même réponse précédente: pthread_cond_broadast ne signale que les threads en attente sur la var de cond .. Si l'un des threads est en cours de traitement alors ils ne se réveilleront pas quand ils arriveront finalement à l'appel d'attente .. – ScaryAardvark

+2

S'ils sont en cours de traitement, ils sont déjà réveillés et ils sortiront sur la prochaine boucle parce que votre démontage() tient le mutex lorsque vous avez terminé, vous êtes en sécurité – nos

+0

+1 pour l'approche du message empoisonné. Cette approche s'est avérée être la meilleure solution dans de nombreux cas. – Tronic

-2

Vous voulez utiliser pthread_cond_broadcast() au lieu de pthread_cond_signal(). Le premier débloque tous les threads en attente d'une condition donnée.

+0

Il échouera toujours si le thread entre dans l'attente après votre appel. – Tronic

+0

Tha est le point .. Il ne fait que quelque chose s'il y a un blocage de thread sur le var var. S'il n'y a pas alors ce fil ne saura pas sortir .. C'est le noeud du problème, – ScaryAardvark

+0

@Tronic: l'affiche originale devrait * encore * utiliser 'pthread_cond_broadcast()' de toute façon, plutôt que le 'pthread_cond_signal() 'boucle. Ma réponse est incomplète (pour le problème actuel, il faut cocher 'finished' avant d'attendre au lieu d'après), mais pas mal. –

-2

Je n'ai jamais utilisé directement pthreads (je préfère Boost.Threads), mais je pense que vous devriez appeler pthread_cancel au lieu de pthread_cond_signal.

+0

Non. Je ne veux pas spécifiquement appeler pthread_cancel, c'est pourquoi j'essaye d'implémenter un joli shutfull gracieux. L'annulation d'un fil de discussion est lourde de problèmes plus graves que ceux que j'essaie de résoudre. – ScaryAardvark

+0

Vous pouvez définir des pthreads à annuler uniquement au point d'annulation, ce qui permet une sortie en douceur. Merci pour le -1, cependant :( – Tronic

+0

Même l'annulation aux points d'annulation est dangereuse, dans de nombreux cas, il peut entraîner des fuites de ressources –

0

Je suppose que vous devriez déverrouiller le mutex après l'appel de pthread_cond_signal. Aussi, s'il vous plaît vérifier la condition de "fini" avant d'entrer en attente conditionnelle après l'acquisition du mutex. J'espère que cela t'aides!

Questions connexes