TP threads POSIX
 

Bertrand Dupouy
Serge Gadret


Les threads POSIX

Sommaire


ATTENTION :

I.1 Exercice 1 : exemple de base

Cet exercice présente les fonctions pthread_create et pthread_join.

Ce qu'il faut faire :

  1. Compléter (en utilisant pthread_create), puis compiler et exécuter le programme contenu dans le fichier Exo0.c .

    Pour compiler : gcc -o Exo0 -Wall Exo0.c -lpthread
    On s'efforcera de retirer tous les warnings.

    Le résultat de la commande Exo0 est un affichage du type :

    
    main (Tid (0x)1 vient de creer : (0x)2
    main (Tid (0x)1 vient de creer : (0x)3
    main (Tid (0x)1 vient de creer : (0x)4
    main (Tid (0x)1 vient de creer : (0x)5
    ------main Fin de main (0x)1
    
    
  2. Pourquoi ne voit-on pas s'afficher les printf effectués dans les threads ?

    Copier le fichier Exo0.c dans un nouveau fichier appelé Exo1.c, modifier ce dernier en utilisant pthread_join et vérifier que les printf sont bien exécutés.

Pour information :
Exemple de création de threads avec passage de paramétres : ici.

I.2 Exercice 2 : utilisation de variables conditionnelles

On veut écrire un programme dans lequel main, comme dans l'exercice précédent, attend la fin de tous les threads qu'il a créé. Mais on ajoute une contrainte :

pthread_join ne convient pas puisqu'elle impose un ordre sur la fin des threads dont on attend la terminaison.
On utilisera donc une variable conditionnelle sur laquelle se bloque main une fois qu'il a créé et lancé tous les threads.
Cette variable conditionnelle sera utilisée par les threads fils pour indiquer leur terminaison.

On donne un canevas de ce programme dans le fichier VarCond.c .

On vous demande :

  1. de compléter ce programme (cf. lignes comportant des ...),
  2. puis de le tester.
    Eventuellement, utiliser la commande truss pour tracer l'exécution.
  3. Ce résultat est-il satisfaisant ? C'est à dire : les threads qui se sont terminés ont-ils tous été acquittés ?
    Indication : pour le vérifier, faire afficher la variable qui compte le nombre d'acquittements.
    Nous allons maintenant remédier à ce dysfonctionnement.
    Comment faire ?
    1. vérifier où se font les prises et les libérations du verrou Verrou :
      • Qui prend la main lorsque l'un des threads créés par main appelle pthread_cond_signal ?
      • Comment se passe la création des threads ?
    2. ajouter un autre verrou,
  4. Modifier le programme et vérifier que tous les threads sont bien acquittés après leur terminaison : la variable qui compte le nombre d'acquittements doit avoir comme valeur le nombre de threads qui se sont terminés.

On donne des corrigés : ici et ici .

I.3. Exercice 3 : accès lecteurs/écrivains à des informations partagées.

On va illustrer l'accès à des données partagées dans la sémantique lecteurs/écrivains (les lectures ne sont pas destructrices, contrairement à l'accès en producteur/consommateur).

SCENARIO :

Ici, il y a une seule donnée partagée qui s'apelle Donnee, utilisée par des threads lecteurs et des threads écrivains.
Les threads écrivains incrémentent la variable partagée de différentes valeurs. Quand cette variable atteint une valeur maximum, donnée par VALMAX, alors tous les threads s'arrêtent, aussi bien les lecteurs que les écrivains.

L'application doit fonctionner de la façon suivante :

  1. le premier lecteur interdit l'accèss aux éventuels écrivains suivants, mais pas aux autres lecteurs,
  2. un écrivain interdit tout autre accès, que ce soit en lecture ou en écriture,
  3. Le point 1 peut provoquer la famine pour les écrivains  : si un écrivain arrive, il doit attendre la fin du flot des lecteurs avant d'accéder à la ressource. Ceci est illustré par le schéma suivant :

    Pour éviter ce dysfonctionnement, qui peut se traduire par la famine pour les écrivains, l'arrivée d'un écrivain doit bloquer tous les lecteurs suivants. Voici le comportement qui sera adopté : lorqu'un écrivain se présente, il attend la sortie des lecteurs courants, les lecteurs qui le suivent sont bloqués. Lorsque tous ces lecteurs sont sortis, l'écrivain modifie la donnée, et on sert le (ou les) écrivain(s) qui seraient arrivés entre-temps.

  4. lorsque un écrivain a fini une mise à jour, il donne d'abord accès aux écrivains qui attendent, puis, si il y en a, les lecteurs passeront après.

On utilise les outils de synchronisation suivants  :


Le programme réalisant cette application se trouve ici . Ce qu'il faut faire :
  1. Compléter la fonction Sortie_SC_Lect
  2. Compléter la fonction Entree_SC_Ecr
  3. Compléter la fonction Sortie_SC_Ecr
  4. Compiler et exécuter le programme avec :