Jointure d'unités d'exécution
Joindre une unité d'exécution signifie attendre qu'elle se termine, ce qui peut être considéré comme une utilisation spécifique des variables de condition.
En attente d'une unité d'exécution
L'utilisation de la sous-routine pthread_join permet à une unité d'exécution d'attendre l'arrêt d'une autre unité d'exécution. Des conditions plus complexes, telles que l'attente de l'arrêt de plusieurs unités d'exécution, peuvent être implémentées à l'aide de variables de condition.
Appel de la sous-routine pthread_join
La sous-routine pthread_join bloque l'unité d'exécution appelante jusqu'à ce que l'unité d'exécution spécifiée se termine. L'unité d'exécution cible (l'unité d'exécution dont l'arrêt est attendu) ne doit pas être déconnectée. Si l'unité d'exécution cible est déjà arrêtée, mais qu'elle n'est pas déconnectée, la sous-routine pthread_join est renvoyée immédiatement. Une fois qu'une unité d'exécution cible a été jointe, elle est automatiquement déconnectée et son stockage peut être récupéré.
Le tableau suivant indique les cas possibles où une unité d'exécution appelle la sous-routine pthread_join , en fonction de l' état et de l'attribut detachstate de l'unité d'exécution cible.
| Etat cible | Cible non déconnectée | Cible déconnectée |
|---|---|---|
| La cible est toujours en cours d'exécution | L'appelant est bloqué jusqu'à ce que la cible soit arrêtée. | L'appel est immédiatement renvoyé, indiquant une erreur. |
| La cible est arrêtée | L'appel est renvoyé immédiatement, ce qui indique que l'opération a abouti. |
Jointures multiples
Plusieurs unités d'exécution peuvent joindre la même unité d'exécution cible, si la cible n'est pas déconnectée. La réussite de cette opération dépend de l'ordre des appels à la sous-routine pthread_join et du moment où l'unité d'exécution cible se termine.
- Tout appel à la sous-routine pthread_join se produisant avant la fin de l'unité d'exécution cible bloque l'unité d'exécution appelante.
- Lorsque l'unité d'exécution cible s'arrête, toutes les unités d'exécution bloquées sont réactivées et l'unité d'exécution cible est automatiquement déconnectée.
- Tout appel à la sous-routine pthread_join survenant après l'arrêt de l'unité d'exécution cible échouera, car l'unité d'exécution est déconnectée par la jointure précédente.
- Si aucune unité d'exécution n'a appelé la sous-routine pthread_join avant l'arrêt de l'unité d'exécution cible, le premier appel à la sous-routine pthread_join est renvoyé immédiatement, ce qui indique que l'opération a abouti et que tout autre appel échoue.
Exemple de jointure
#include <pthread.h> /* include file for pthreads - the 1st */
#include <stdio.h> /* include file for printf() */
void *Thread(void *string)
{
int i;
/* writes five messages and exits */
for (i=0; i<5; i++)
printf("%s\n", (char *)string);
pthread_exit(NULL);
}
int main()
{
char *e_str = "Hello!";
char *f_str = "Bonjour !";
pthread_attr_t attr;
pthread_t e_th;
pthread_t f_th;
int rc;
/* creates the right attribute */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,
PTHREAD_CREATE_UNDETACHED);
/* creates both threads */
rc = pthread_create(&e_th, &attr, Thread, (void *)e_str);
if (rc)
exit(-1);
rc = pthread_create(&f_th, &attr, Thread, (void *)f_str);
if (rc)
exit(-1);
pthread_attr_destroy(&attr);
/* joins the threads */
pthread_join(e_th, NULL);
pthread_join(f_th, NULL);
pthread_exit(NULL);
}Une unité d'exécution ne peut pas se joindre à elle-même car un interblocage se produit et elle est détectée par la bibliothèque. Cependant, deux unités d'exécution peuvent essayer de se joindre. Ils vont se bloquer, mais cette situation n'est pas détectée par la bibliothèque.
Renvoi d'informations à partir d'une unité d'exécution
La sous-routine pthread_join permet également à une unité d'exécution de renvoyer des informations à une autre unité d'exécution. Lorsqu'une unité d'exécution appelle la sous-routine pthread_exit ou lorsqu'elle revient de sa routine de point d'entrée, elle renvoie un pointeur (voir Sortie d'une unité d'exécution). Ce pointeur est stocké tant que l'unité d'exécution n'est pas détachée et que la sous-routine pthread_join peut le renvoyer.
/* "scanning" thread */
...
buffer = malloc(...);
/* finds the search pattern in the file
and stores the lines in the buffer */
return (buffer);
/* initial thread */
...
for (/* each created thread */) {
void *buf;
pthread_join(thread, &buf);
if (buf != NULL) {
/* print all the lines in the buffer,
preceded by the filename of the thread */
free(buf);
}
}
...Si le thread cible est annulé, la sous-routine pthread_join renvoie une valeur de -1 transformée en pointeur (voir Annulation d'un thread). Étant donné que -1 ne peut pas être une valeur de pointeur, l'obtention de -1 en tant que pointeur retourné par un thread signifie que le thread a été annulé.
Le pointeur renvoyé peut pointer vers n'importe quel type de données. Le pointeur doit toujours être valide après l'arrêt de l'unité d'exécution et sa récupération de mémoire. Par conséquent, évitez de renvoyer une valeur, car la routine de destructeur est appelée lorsque la mémoire de l'unité d'exécution est récupérée.
void *returned_data;
...
pthread_join(target_thread, &returned_data);
/* retrieves information from returned_data */
free(returned_data);free(returned_data);/* lock - entering a critical region, no other thread should
run this portion of code concurrently */
if (!flag) {
free(returned_data);
flag = 1;
}
/* unlock - exiting the critical region */Le verrouillage de l'accès à la région critique garantit que le pointeur returned_data n'est libéré qu'une seule fois.
Lorsque vous renvoyez un pointeur vers un espace de stockage alloué de manière dynamique à plusieurs unités d'exécution exécutant un code différent, vous devez vous assurer qu'une seule unité d'exécution libère le pointeur.