Articles récents
Linux/Unix : Les threads
A la différence des processus, le thread ne dispose pas d'un espace mémoire dédié. Il doit mettre en place des mécanismes pour assurer les entrées/sorties au moment opportun. Nous étudierons ces mécanismes de verrouillage de la mémoire.
Pourquoi ce partage de l'espace mémoire? C'est un constat, dans le monde des processus on réserve beaucoup de mémoire qui est souvent peu utilisée, alors qu'il s'agit d'une ressource rare et coûteuse. Le thread dispose de l'ensemble de l'espace mémoire disponible, cela permet d'optimiser l'espace mémoire au plus juste. Mais cela implique que les différents threads doivent se gérer entre eux pour éviter les conflits d'écriture.
En POSIX, une application peut être composé de plusieurs processus, eux-même composés de plusieurs thread. La norme POSIX réalise des threds qui sont portables d'une plateforme à une autre, on parle des POSIX thread ou des P-thread.
Pour compiler un code qui traite des thread, il y a une syntaxe particulière:
1)Les activités
On confond la dénomination activité et thread, c'est la même chose.1.1)Les attributs d'une activité
Une activité possède un identifiant TID (Thread Identity) de type pthread_t qui est un entier. Ce numéro est complémentaire du PID pour identifier le processus.La fonction getpid() retourne l'identifiant du processus UNIX:
La fonction pthread_self() retourne l'identité de l'activité en cours (thread actif):
La fonction pthread_equal() permet de tester légalité entre deux identités tid1 et tid2, valeur (nulle si oui):
1.2)création/suppression d'une activité
Pour créer un thread, il suffit de faire un appel à la fonction pthread_create(). Elle retourne 0 en cas de succès et -1 sinon (la variable errno permet de connaitre l'erreur rencontrée). Si l'appel est réussi, *thread reçoit l'identité de l'activité. start_routine correspond à la fonction qui sera exécutée par l’activité et arg correspond au paramètre de la fonction start_routine. A noter que le paramètre attr définit les attributs de l'activité, avec la valeur NULL pour une utilisation par défaut. Sinon, on peut utiliser la fonction pthread_attr_init() pour initialiser la paramètre attr.Voici un exemple de création de thread:
Ce qui donne:
Pour arrêter une activité, nous avons exit() et _exit(). Si le processus est arrêté, toutes les activités associées sont terminées. Pour arrêter une activité, il y aussi la fonction pthread_exit() qui termine l'activité appelante.
Lorsqu'une activité se termine, elle ne disparaît pas totalement et les ressources en mémoires ne sont pas libérées, il faut pour cela faire un appel à la fonction pthread_join(). Elle attend la fin du thread pour libérer les ressources, il faut donc l'appeler depuis le processus père. Le paramètre th référence le thread à attendre et thread_return le code de retour éventuellement transmis par pthread_exit().
On ajoute à l'exemple précédent la libération des ressources:
1.3)Demande de libération des ressources
Nous avons vu la fonction pthread_join() qui libère les ressources lorsque l'activité se termine. Il existe d'autres fonctions comme pthread_detach() qui demande la libération des ressources au processus, auquel elle appartient, à la fin de vie de l'activité.Il est impératif que les ressources soient libérées par un appel à la fonction pthread_join() ou pthread_detach().
La fonction pthread_cancel() demande l'abandon d'une autre activité, l'activité choisira si elle réalise l'abandon ou non avec les méthodes pthread_setcancelstate() ou pthread_setcanceltype().
2)Les mutex
Un mutex permet de verrouiller une zone critique pour assurer l'unicité du travail d'une activité. C'est un mécanisme trés puissant que nous allons étudier ci-dessous.2.1)création d'un mutex
Avant l'appel, le mutex pointe sur la zone réservée du futur mutex, mutteattr est l'ensemble des attributs à affecter au mutex, soit NULL par défaut. Après l'appel, le nouveau mutex pointe sur la zone dédiée. La fonction retourne 0 en cas de succès.
La primitive P pour un mutex:
L'appel bloquant pthread_mutex_lock() réserve un mutex ou attend que le mutex se libère pour le réserver au profit du thread appelant. La fonction retourne 0 en cas de succès.
Il existe un appel non bloquant (test et passe à la suite, pas d'attente contrairement à la fonction précédente) qui est assuré par la fonction pthread_mutex_trylock(). 0 en cas de succès (errno=EBUSY si occupé et errno=EINVAL si le mutex n'est pas initialisé
La primitive V pour un mutex:
La fonction pthread_mutex_unlock() permet de débloquer une activité en attente sur ce mutex. Elle retourne 0 en cas de succès.
2.2)Destruction d'un mutex
Pour détruire un mutex, il y a la fonction pthread_mutex_destroy().2.3)Exemple d'utilisation
Voici un exemple d'utilisation de l'utilisation d'un mutex. On créé trois fonctions qui attendent la fin de la précédente pour se lancer.Ce qui donne:
3)Les conditions autour du mutex
La condition des mutex permet de synchroniser plusieurs activités dans une section critique. Le mécanisme générale :3.1)Initialisation d'une condition
Il faut préparer la condition en appelant la fonction pthread_cond_init(), cond est un pointeur sur l'espace mémoire réservé à la condition et cond_attr précise les attributs de la condition. Après l'appel, cond pointera sur la nouvelle condition. La fonction retourne 0 en cas de succès.3.2)Attente sur une condition
La fonction pthread_cond_wait a un fonctionnement assez particulier:Le problème de la fonction précédente est que l'attente n'est pas bornée, elle peut être infinie! Aussi, il existe une fonction qui attend un temps limité. On lui passe un objet abstime pour définir le temps à patienter.
3.3)Envoi d'un signal
Cela permet d'activer un thread en attente sur une condition.Il est également possible d'envoyer un signal à toutes les activités:
3.4)Suppression d'une condition
3.5)Exemple
Voici un exemple de code ou quatre thread doivent se partager 2 outils pour travailler, et donc il y a des threads en attente.Ce qui donne:
4) Autres fonctions relatives
4.1) Demande explicite de libération
Le système Unix ou Linux dispose d'un ordonnanceur, qui permet de gérer les activités multi-threading. Il s'agit d'un mécanisme autonome, mais fort heureusement il existe un fonction pour agir dessus. Cela se fait par la fonction sched_yield(), ainsi en l'utilisant, un thread peut demander de rendre la main (fonctionne également avec les processus).4.2) Appel unique à une fonction
Il existe un mécanisme qui permet de lancer une fonction une seule fois, utile notamment dans un milieu multi-thread pour initialiser les variables. Il est nécessaire pour ce faire d'initialiser une variable pthread_once_t avec PTHREAD_ONCE_INIT puis on utilise la fonction pthread_once(). Voici un exemple:5)Gestion des thread sous Linux
Linux ne distingue pas les processus et les thread au niveau du noyau, c'est juste le contexte d’exécution qui fait que la mémoire est partagée ou pas. En fait le fork() ou le pthread_create() font appel au même appel système clone(). Mais pour respecter la norme POSIX, le noyau Linux a introduit les particularités suivantes:Conclusion
Voila vous avez les bases pour commencer à coder des scripts en multi-threading! Attention de bien sécuriser vos variables globales.
© 2024 www.doritique.fr par Robert DORIGNY