Cours ‐ Threads - vbridonneau/CoursSysteme GitHub Wiki
Le concept que nous allons voir ici s'inscrit dans la continuité de ce que nous avons vu avec les processus. Nous allons voir ici les threads qui sont, dans un sens, des processus légers. A la différence des processus que nous avons vu jusqu'à présent, les threads peuvent être vus comme des processus qui partagent leurs mémoires entre eux. Les threads possèdent certains avantages comme un changement de contexte moins coûteux que pour les processus, un découpage des tâches plus faciles, etc. .
Fonction | Description |
---|---|
pthread_create |
Création d'un thread |
pthread_exit |
Sortie d'un thread |
pthread_join |
Attente d'un thread depuis le thread principal |
pthread_attr_init |
Initialisation des attributs d'un thread |
pthread_attr_destroy |
Destruction des attributs d'un thread |
pthread_mutex_init |
Initialisation dynamique d'un mutex |
pthread_mutex_destroy |
Destruction d'un mutex créé par pthread_mutex_init
|
Notes : La compilation d'un programme utilisant l'une des fonctions de la bibliothèque pthread
nécessite de renseigner l'option -pthread
à gcc afin de lier la bibliothèque à un programme.
La création d'un thread se fait via un appel à la fonction pthread_create
dont le prototype est le suivant :
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine) (void *),
void *arg
);
Voyons en détail le rôle de chacun des arguments :
Argumrent | Rôle |
---|---|
thread |
Récupère l'identifiant du thread |
attr |
Sert à paramétrer la création d'un thread |
start_routine |
Fonction que le thread créé devra appeler |
arg |
Argument à passer à la fonction |
Plusieurs choses sont à notés ici.
Premièrement, lorsque l'on créé un thread, on doit lui récupérer son identifiant pour pouvoir l'attendre à la fin (voir pthread_join
plus loin).
Ensuite, il est possible de paramétrer la création d'un thread via la variable attr
qui peut servir à renseigner si le thread est détaché ou non ou à donner la taille de la pile par exemple.
Enfin, et c'est une des différences avec les processus, un thread ne commence pas son exécution à l'endroit où pthread_create
est appelée.
Un thread commence son exécution en appelant directement la fonction qui lui est passée en paramètre.
C'est à cela que sert le paramètre start_routine
: c'est un pointeur de fonction qui contient l'adresse de la fonction à appeler.
Si on regarde attentivement, la fonction start_routine
que l'on passe en paramètre, prend un argument en paramètre.
Cet argument est donné via le paramètre arg
à la fonction lors de son appel.
Si l'on ne souhaite pas en donner, il faut quand même que la fonction que l'on passe en paramètre prenne un argument et on passe à pthread_create
NULL
.
Enfin la fonction start_routine
renvoit une valeur sous la forme d'un void*
ce qui veut dire que la fonction peut renvoyer un pointeur vers n'importe quelle type.
Cette valeur de retour sera passée ensuite récupérer par le thread ayant créé celui appelant pthread_create
via la fonction pthread_join
.
Notes : souvent, lorsque l'on chereche à créer un thread, on crée une structure dédiée afin d'être moins contraint par le champ arg
.
Pour sortir d'un thread on utilise la fonction pthread_exit
ou on effectue simplement une instruction return
à la fin de la fonction que l'on passe à pthread_create
.
Le prototype de pthread_exit
est le suivant :
#include <pthread.h>
void pthread_exit(void *retval);
Cette fonction est appelée pour sortir d'un thread et renvoyer au thread principal une valeur d'un type quelconque via l'argument retval
.
Pour récuper cette valeur, et de manière générale pour attendre un thread depuis un thread principal, on utilise la fonction pthread_join
dont voici le prototype :
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
Les deux arguments de cette fonction sont :
-
thread
: l'idenfiant du thread que l'on attend. -
retval
: un pointeur vers une variable pour récupérer le résultat passé en argument àpthread_exit
. Si on souhaite ignorer cette valeur, il suffit de passerNULL
à la fonction.
Cette fonction est l'équivalent de wait
avec les processus, les différences étant que l'on doit impérativement préciser quel thread est attendue et que l'on peut récupérer facilement un résultat à la fin de l'exécution d'un thread via le second paramètre de pthread_join
.
Lorsque l'on crée un thred, il est possible que l'on ait des besoins spécifiques.
Par exemple, il est possible de vouloir dire que l'on sait à l'avance qu'un thread ne sera pas joignable ou que l'on souhaite plus d'espace pour la pile d'appel.
Pour répondre à ce besoin, il est possible de changer les attributs d'un threads via la structure pthread_attr_t
.
L'implémentation de cette structure dépend du système d'exploitation.
Ainsi, pour garantir au mieux la portabilité d'un programme, il est recommandé d'utiliser les fonctions de la famille pthread_attr_*
qui permette de mettre à jour cette structure.
Parmis elles, on trouve notamment les deux fonctions servant à initialiser et à détruire les attibuts de thread : pthread_attr_init
et pthread_attr_destroy
respectivement.