Fonction pthread_join dans le langage C avec des exemples de threads simples et multiples

Fonction pthread_join dans le langage C avec des exemples de threads simples et multiples
La langue C offre la possibilité de développer les programmes multitâches en utilisant la bibliothèque Pthread de la norme POSIX. Cette méthode augmente la vitesse du programme fini en exécutant plusieurs threads en parallèle avec la fonction principale.

Pour créer un programme réussi en utilisant cette méthode de programmation, vous devez faire des considérations spéciales. Par exemple, si les temps d'exécution d'un thread sont plus longs que ceux de la fonction principale () et qu'il est entièrement exécuté, le programme se terminera et tous les threads associés, qu'ils aient terminé leur tâche ou non.

Cela peut causer des problèmes critiques car un thread qui se termine de façon inattendue pourrait être en train d'enregistrer des données générées par l'utilisateur, d'écrire dans un fichier ou un appareil, ou d'effectuer une autre tâche importante.

Dans cet article de Linux Hint, vous apprendrez à utiliser la fonction PTHEAD_JOIN () pour vous assurer qu'un ou plusieurs threads sont terminés de manière fiable avant que le programme ne sache ou reprend.

Syntaxe de la fonction pthread_join () dans le langage C

int pthread_join (thread pthread_t, void ** retval);

Description de la fonction pthread_join () dans le langage C

La fonction pthread_join () attend le processus à partir duquel il est appelé jusqu'à ce qu'un thread ait terminé sa tâche. Cette fonction suspend la fonction à partir de laquelle il est appelé jusqu'à ce que le sous-processus soit spécifié par son identifiant dans l'argument d'entrée de thread se termine.

Une fois que le fil a terminé sa tâche, le programme reprend l'exécution de la ligne suivante du code qui appelle le pthread_join ().

Lorsque le sous-processus se termine, la fonction pthread_join () renvoie l'état de sortie du thread dans l'argument RETVAL.

Ensuite, nous voyons les arguments d'entrée au pthread_join () avec une description de la fonction qui satisfait chacun de ces arguments:

fil de discussion: Cet argument d'entrée est une valeur de données du type pthread_t et est l'identifiant du sous-processus que la fonction pthread_join () devrait s'attendre.

La valeur de l'identificateur est générée par le système et est obtenue à la suite de la fonction PTHEAD_CREATE () dans son thread d'argument de sortie lorsque le thread est créé.

retval: Cet argument d'entrée est un pointeur vers (void *) où la fonction pthread_join () stocke l'état de sortie du thread.

Si pthread_join () revient avec succès, il renvoie 0 comme le résultat. Si une erreur se produit, cette fonction renvoie un entier avec une valeur qui représente l'erreur qui s'est produite. Plus tard, vous verrez une section spéciale qui décrit toutes les erreurs que cette fonction peut générer et comment les identifier.

La fonction pthread_join () est définie dans le pthread.tête H. Pour l'utiliser, les en-têtes suivants doivent être inclus dans le «.Fichier C ”, comme indiqué dans ce qui suit:

#inclure
#inclure

Remarque: il est important que vous n'utilisiez cette fonctionnalité que si nécessaire, car vous pouvez prolonger le temps d'exécution d'un programme en arrêtant un processus pour attendre que l'autre termine.

Erreurs de compilation dans les programmes avec des threads

Lors de la compilation des programmes GCC qui utilisent des threads, la compilation peut échouer si elle n'est pas effectuée correctement à partir de la ligne de commande.

Le message d'erreur le plus courant émis par le compilateur indique que l'une des fonctions de thread auxquelles nous nous référons dans le code n'est pas définie.

Ces erreurs entraînent souvent un temps précieux pour vérifier les en-têtes que nous avons insérés dans le code, leur intégrité et les répertoires associés au compilateur car tout indique que le problème est là.

Bien que les fonctions qui provoquent l'erreur soient définies dans le «pthread.En-tête h ”et sont inclus dans le code, le compilateur ne reconnaît pas ces fonctions à moins que la bibliothèque Pthread ne soit appelée à partir de la ligne de commande pendant la compilation.

Dans l'illustration suivante, vous pouvez voir la bonne façon d'appeler la bibliothèque Pthread (mise en évidence en vert) de la console de commande lors de la compilation de programmes avec des threads:

~ $ gcc -pthread path / nom de fichier.c -o out_name

Comme nous pouvons le voir dans la figure suivante, l'erreur disparaît lorsque la bibliothèque Pthread est appelée pendant la compilation.

Comment utiliser la fonction pthread_join () pour attendre qu'un thread termine l'exécution dans le langage C

Dans cet exemple, nous créons un thread qui a un temps d'exécution plus long que la fonction principale (). Après avoir créé le thread, nous utilisons le pthread_join () pour attendre l'exécution entière du thread avant de quitter la fonction principale () et de terminer le programme.

À cette fin, nous créons la fonction thread_f () qui exécute le thread. Cette fonction est une boucle «pour» avec 5 cycles d'une seconde durée, chacune imprime le «Quelques secondes à la fin du fil:”Message, suivi du nombre de secondes restantes dans la console de commande.

À partir de la fonction main (), nous créons le thread_1 avec la fonction pthread_create (). Ensuite, nous appelons la fonction pthread_join () pour attendre que le sous-processus termine l'exécution avant de quitter le programme.

L'appel de fonction pthread_join () va avec l'identifiant thread_1 comme le premier argument d'entrée et le retval comme nul dans le deuxième argument. Nous voyons le code complet suivant pour cet exemple:

#inclure
#inclure
#inclure
#inclure
void * thread_f (void * n);
int main()

pthread_t thread_1;
pthread_create (& thread_1, null, thread_f, null);
pthread_join (thread_1, null);
retour 0;

void * thread_f (void * n)

pour (int a = 5; a!= 0; un--)

printf ("secondes à la fin du thread:% i \ n", a);
sommeil (1);

printf ("fin de thread \ n");
pthread_exit (n);

L'image suivante montre la compilation et l'exécution de ce code:

Comme nous l'avons vu sur la figure, la boucle «For» a terminé ses 5 cycles et le programme est fermé après que Thread_1 a terminé son exécution. Voyons ce qui se passe si nous supprimons la fonction pthread_join (). Compiler et exécuter le code suivant:

Comme nous pouvons le voir sur la figure, la boucle «pour» de Thread_1 n'a pas pu compléter une réussite avant que la fonction Main () ne soit entièrement exécutée et que le programme soit terminé.

Comment utiliser la fonction pthread_join () pour attendre qu'un thread multiple pour terminer l'exécution dans le langage C

Dans cet exemple, nous vous montrerons comment utiliser la fonction pthread_join () pour attendre que plusieurs threads simultanés se terminent.

Pour ce faire, nous utilisons le code de l'exemple précédent et ajoutons un deuxième thread qui est thread_2 et une deuxième routine qui est thread_f_2 () qui s'exécute en parallèle avec Thread_1. Le temps d'exécution de cette routine est deux fois plus long que celui de thread_f_1 () qui prend 10 secondes.

La méthode que nous utilisons consiste à créer les deux threads l'un après l'autre en utilisant la fonction pthread_create (). Ensuite, nous appelons d'abord la fonction pthread_join () avec le thread en cours d'exécution le plus court, suivi d'un nouvel appel à cette fonction avec le fil le plus long en cours d'exécution. Voici le code complet de cet exemple:

#inclure
#inclure
#inclure
#inclure
void * thread_f_1 (void * n);
void * thread_f_2 (void * n);
int main()

pthread_t thread_1;
pthread_t thread_2;
pthread_create (& thread_1, null, thread_f_1, null);
pthread_create (& thread_2, null, thread_f_2, null);
pthread_join (thread_1, null);
pthread_join (thread_2, null);
retour 0;

void * thread_f_1 (void * n)

pour (int a = 5; a!= 0; un--)

printf ("secondes à la fin du thread 1:% i \ n", a);
sommeil (1);

printf ("fin du thread 1 \ n");
pthread_exit (n);

void * thread_f_2 (void * n)

pour (int a = 10; a!= 0; un--)

printf ("secondes à la fin du thread 2:% i \ n", a);
sommeil (1);

printf ("fin du thread 2 \ n");
pthread_exit (n);

Comme nous le voyons dans l'image suivante, les deux threads ont terminé leur exécution:

Erreurs que la fonction pthread_join () peut renvoyer: que sont-ils et comment peuvent-ils être détectés?

Lorsqu'une erreur se produit, la fonction pthread_join () renvoie l'un des codes prédéfinis suivants:

ESRCH: L'identifiant spécifié n'est associé à aucun fil.

Einval: Le thread n'est pas jointable ou un autre thread attend déjà de rejoindre celui-ci.

EDEADLK: Un crash est détecté.

Ces erreurs sont prédéfinies dans le «errno.en-tête h ”et sont renvoyés en conséquence dans la fonction de sortie de la fonction pthread_join (), pas dans la variable globale Errno.

Voici le code de l'exemple précédent où nous ajoutons une condition «IF» pour déterminer si une erreur s'est produite. Si c'est le cas, le programme entre dans une condition de commutation qui identifie l'erreur et affiche un message dans la console de commande avec la description spécifique de cette erreur.

Nous incluons également le «Errno.En-tête h ”et déclarer la variable d'erreur qui seront les conditions« IF »et« commutation ».

Pour générer l'erreur, nous nous référons au thread_2 dans l'argument d'entrée de thread de la fonction pthread_join () qui n'est pas créé.

#inclure
#inclure
#inclure
#inclure
#inclure
void * thread_f (void * n);
int main()
Dans la terreur;
pthread_t thread_1;
pthread_t thread_2;
pthread_create (& thread_1, null, thread_f, null);
error = pthread_join (thread_2, null);
if (erreur!= 0)
commutateur (erreur)
cas Esrch:
printf ("L'identifiant n'est associé à aucun thread \ n");
casser;
cas Einval:
printf ("thread non joinable ou un autre thread attend déjà \ n");
casser;
cas EDEADLK:
printf ("Un crash a été détecté.\ n ");
casser;

retour 0;

void * thread_f (void * n)

pour (int a = 5; a!= 0; un--)

printf ("secondes à la fin du thread:% i \ n", a);
sommeil (1);

printf ("fin de thread \ n");
pthread_exit (n);

L'image suivante montre la compilation et l'exécution de ce code. Voyez comment le programme entre dans l'instruction «IF» et l'instance ESRCH de l'instruction Switch qui indique que l'identifiant Thread_2 pointe vers un thread inexistant. Vous pouvez également voir que l'exécution de Thread_1 est incomplète.

Conclusion

Dans cet article de Linux Hint, nous vous avons montré comment implémenter la fonction pthread_join () de la bibliothèque de threads pour assurer une exécution complète d'un sous-processus.

Nous avons expliqué la syntaxe de cette fonction et décrit chacun de ses arguments d'entrée et comment obtenir les données qui doivent y être transmises. Nous avons ensuite implémenté la fonction dans un exemple pratique avec des extraits de code et des images pour montrer comment vous pouvez utiliser la fonction pthread_join () pour assurer une exécution complète et fiable d'un thread.

Nous vous avons également montré comment lier correctement la bibliothèque de threads pour une compilation sans erreur. Dans une section spéciale, nous vous avons montré comment détecter et identifier les erreurs que la fonction pthread_join () peut générer.