Sorbonne Université Master 2017 2018
PR : Travaux sur Machine 4. Introduction aux processus légers

Cette séance consiste à mettre en pratique la notion de processus léger, en particulier pour comprendre les phénomènes de concurrence induits, et les gérer par les mécanismes de synchronisation qui leur sont propres. On utilisera pour cela la bibliothèque pthreads à inclure par pthread.h. On trouvera en annexe un Makefile permettant de gérer ses fichiers, toutefois selon les systèmes, il faudra parfois l’utiliser ainsi :

LDFLAGS=’-lpthread’ make but

ou le modifier pour donner systématiquement cette valeur à LDFLAGS.

Par ailleurs, les fonctions sur les processus légers, en cas d’erreur, n’affectent pas la globale errno (car elle peut être modifiée à tout moment par un autre processus léger) : on signalera donc les erreurs non avec perror et errno, mais en écrivant sur le flux des erreurs le résultat des dites fonctions quand il est non nul.



1 Création de threads

Ecrire un programme prenant en argument un nombre N et créant N processus légers (à l’aide de pthread_create) et passant en paramètre à chacun son numéro de création compris entre 0 et N. Chacun affichera son numéro de création et son identité (utiliser pthread_self) puis se termine avec pthread_exit, en retournant son numéro de création multiplié par 2. De son côté, après création de tous ces processus, le programme principal doit attendre leur terminaison (à l’aide de pthread_join) en affichant la valeur renvoyée par chacun.

Exemple d'appel :
$PWD/bin/thread_create 10
Fichier à créer : src/thread_create.c

2 Exclusion mutuelle de threads

Modifier le programme de l’exercice précédent pour que chaque Thread reçoive, non plus son numéro de création mais l’adresse d’une variable locale à la fonction main, préalablement initialisé à 0. Chaque Thread calcule une valeur aléatoire entre 0 et 10 à l’aide de la fonction rand de la façon suivante :

(int) (10*((double)rand())/ RAND_MAX)

Cette valeur est alors ajoutée à la variable passée en argument. On veillera évidemment à éviter les accès concurrents à cette variable, en utilisant les fonctions de la famille pthread_mutex_lock, et on examinera son évolution en affichant ses valeurs successives, la valeur aléatoire et l’identifiant du Thread qui l’a calculée. Après terminaison de toutes les Threads, le programme affiche la valeur finale de cette variable.

Exemple d'appel :
$PWD/bin/thread_rand 10
Fichier à créer : src/thread_rand.c

3 Réveil de threads

Modifier le programme précédent pour que la valeur finale soit affichée non plus par le programme principal mais par un nouveau Thread créé au départ. Celui-ci doit immédiatement se bloquer en attendant que la somme de toutes les valeurs aléatoires soit complétée. Le dernier Thread à ajouter sa valeur aléatoire utilisera pthread_cond_signal pour signifier au premier qu’il peut afficher le résultat.

Remarque : il faut prévoir le cas où le premier Thread commence son exécution après terminaison de tous les autres, donc après envoi du signal. On peut provoquer cette situation en appelant sleep au début de son exécution.

Exemple d'appel :
$PWD/bin/thread_wait 10
Fichier à créer : src/thread_wait.c

4 Annexes


makefile
Format de l'envoi : src/thread_create.c
Répertoires autorisés : src/thread_rand.c src/thread_wait.c