web-dev-qa-db-fra.com

Comment interrogez-vous un pthread pour voir s'il est toujours en cours d'exécution?

Dans mon destructeur, je veux détruire un fil proprement.

Mon but est d'attendre la fin de l'exécution d'un thread, puis de le détruire.

La seule chose que j'ai trouvée sur l'interrogation de l'état d'un pthread est pthread_attr_setdetachstate mais cela ne vous indique que si votre fil est:

  • PTHREAD_CREATE_DETACHED 
  • PTHREAD_CREATE_JOINABLE

Les deux n'ont rien à voir avec si le thread est toujours en cours d'exécution ou non.

Comment interrogez-vous un pthread pour voir s'il est toujours en cours d'exécution?

44
Trevor Boyd Smith

On dirait que vous avez deux questions ici:

Comment puis-je attendre la fin de mon thread?

Réponse: Ceci est directement pris en charge par pthreads - créez votre thread à arrêter arrêté JOINABLE (lors de son premier démarrage) et utilisez pthread_join () pour bloquer votre thread actuel jusqu'à ce que le thread à arrêter ne soit plus fonctionnement.


Comment puis-je savoir si mon fil est toujours en cours d'exécution?

Réponse: Vous pouvez ajouter un drapeau "thread_complete" pour faire l'affaire:

Scénario: le fil A veut savoir si le fil B est toujours en vie.

Lorsque le thread B est créé, un pointeur sur l'adresse de l'indicateur "thread_complete" lui est attribué. L'indicateur "thread_complete" doit être initialisé à NOT_COMPLETED avant la création du thread. La fonction de point d'entrée du thread B doit immédiatement appeler pthread_cleanup_Push () pour envoyer un "gestionnaire de nettoyage" qui définit l'indicateur "thread_complete" sur COMPLETED.

Voir les détails concernant les gestionnaires de nettoyage ici: pthread gestionnaires de nettoyage

Vous voudrez inclure un appel pthread_cleanup_pop (1) correspondant pour vous assurer que le gestionnaire de nettoyage est appelé quel que soit le cas (c'est-à-dire si le thread se ferme normalement OR en raison d'une annulation, etc.).

Ensuite, le thread A peut simplement vérifier le drapeau "thread_complete" pour voir si le thread B s'est déjà terminé.

NOTE: Votre drapeau "thread_complete" devrait être déclaré "volatile" et devrait être un type atomique - les compilateurs GNU fournissent le sig_atomic_t à cette fin. Cela permet aux deux threads d'accéder de manière cohérente aux mêmes données sans recourir à des constructions de synchronisation (mutexes/sémaphores).

35
jeremytrimble
pthread_kill(tid, 0);

Aucun signal n'est envoyé, mais le contrôle des erreurs est toujours effectué afin que vous puissiez l'utiliser pour vérifier l'existence du fichier tid.

CAUTION: Cette réponse est incorrecte. La norme interdit spécifiquement de transmettre l'ID d'un thread dont la durée de vie est terminée. Cet ID peut maintenant spécifier un autre thread ou, pire, il peut faire référence à la mémoire libérée, provoquant un blocage.

30
pthread_kill

Je pense que tout ce dont vous avez besoin est d'appeler pthread_join (). Cet appel ne reviendra pas tant que le fil ne sera pas sorti.

Si vous voulez seulement interroger pour savoir si le thread est toujours en cours d'exécution (et notez que ce n'est généralement pas ce que vous devriez vouloir faire!), Vous pouvez lui demander de définir un booléen volatil sur false juste avant de le quitter. alors votre thread principal pourrait lire le booléen et si c'est toujours vrai, vous savez que le thread est toujours en cours d'exécution. (si elle est fausse, par contre, vous savez que le fil est au moins presque terminé; il est peut-être toujours en train d'exécuter du code de nettoyage après avoir défini la valeur booléenne sur false, donc même dans ce cas, vous devez toujours appeler pthread_join essayant de libérer les ressources auxquelles le fil pourrait avoir accès)

8
Jeremy Friesner

Il n'y a pas de solution entièrement portable, regardez si votre plate-forme supporte pthread_tryjoin_np ou pthread_timedjoin_np. Donc, vous venez de vérifier si le thread peut être rejoint (bien sûr créé avec PTHREAD_CREATE_JOINABLE).

5
Dewfy

Je pense avoir mis au point une solution qui fonctionne au moins pour Linux. Chaque fois que je crée un fil, je l’ai enregistré, c’est LWP (Light Weight Process ID) et lui attribue un nom unique, par exemple . int lwp = syscall (SYS_gettid); prctl (PR_SET_NAME, (long) "nom unique", 0, 0, 0);

Ensuite, pour vérifier si le fil existe plus tard, ouvrez/proc /pid/ tâche /lwp/ comm et le lis. Si le fichier existe et que son contenu correspond au nom unique que j'ai attribué, le thread existe. Notez que cela ne transmet PAS un TID éventuellement obsolète/réutilisé à une fonction de bibliothèque, donc pas de crash.

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/file.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <syscall.h>

pthread_t subthread_tid;
int       subthread_lwp;

#define UNIQUE_NAME "unique name"

bool thread_exists (pthread_t thread_id)
{
    char path[100];
    char thread_name[16];
    FILE *fp;
    bool  thread_exists = false;

    // If the /proc/<pid>/task/<lwp>/comm file exists and it's contents match the "unique name" the
    // thread exists, and it's the original thread (TID has NOT been reused).

    sprintf(path, "/proc/%d/task/%d/comm", getpid(), subthread_lwp);

    fp = fopen(path, "r");

    if( fp != NULL ) {

        fgets(thread_name, 16, fp);
        fclose(fp);

        // Need to trim off the newline
        thread_name[strlen(thread_name)-1] = '\0';

        if( strcmp(UNIQUE_NAME, thread_name) == 0 ) {
            thread_exists = true;
        }
    }

    if( thread_exists ) {
        printf("thread exists\n");
    } else {
        printf("thread does NOT exist\n");
    }

    return thread_exists;
}


void *subthread (void *unused)
{
    subthread_lwp = syscall(SYS_gettid);
    prctl(PR_SET_NAME, (long)UNIQUE_NAME, 0, 0, 0);

    sleep(10000);

    return NULL;
}


int main (int argc, char *argv[], char *envp[])
{
    int error_number;

    pthread_create(&subthread_tid, NULL, subthread, NULL);
    printf("pthread_create()\n");
    sleep(1);
    thread_exists(subthread_tid);

    pthread_cancel(subthread_tid);
    printf("pthread_cancel()\n");
    sleep(1);
    thread_exists(subthread_tid);

    error_number = pthread_join(subthread_tid, NULL);
    if( error_number == 0 ) {
        printf("pthread_join() successful\n");
    } else {
        printf("pthread_join() failed, %d\n", error_number);
    }
    thread_exists(subthread_tid);

    exit(0);
}
0
Eric Buell

Permettez-moi de noter la réponse «gagnante», qui comporte un énorme défaut caché et qui, dans certains contextes, peut entraîner des accidents. Sauf si vous utilisez pthread_join, il reviendra encore et encore. Supposons que vous rencontrez un processus et une bibliothèque partagée. Appelez la bibliothèque lib.so.

  1. Vous l'ouvrez, vous commencez un fil dedans. Supposons que vous ne vouliez pas qu'il y soit associé, vous le définissez donc de manière amovible.
  2. Processus et logique partagée de la bibliothèque faisant son travail, etc ...
  3. Vous voulez charger lib.so, parce que vous n'en avez plus besoin.
  4. Vous appelez un arrêt du thread et vous dites que vous souhaitez lire un indicateur à partir du thread de votre lib.so, qu'il est terminé.
  5. Vous continuez sur un autre fil avec dlclose, car vous voyez que vous avez vu que le drapeau indique maintenant que le fil est "terminé"
  6. dlclose chargera toute la mémoire liée à la pile et au code.
  7. Whops, mais dlclose n'arrête pas les threads. Et vous savez, même lorsque vous êtes dans la dernière ligne du gestionnaire de nettoyage pour définir la variable de drapeau atomique volatile "le fil est fini", vous devez toujours renvoyer de nombreuses méthodes de la pile, en rendant des valeurs, etc. une priorité énorme a été donnée au fil # 5 + 6, vous recevrez un message avant de pouvoir VRAIMENT vous arrêter sur le fil. Vous aurez parfois des accidents de Nice.

Permettez-moi de souligner que ce n'est pas un problème hipothetical, j'ai eu le même problème sur notre projet.

0
newhouse