TD 6 - Threads and concurrency

1. Threads

Le but de cet exercice est la manipulation des fonctions de création et d’attente des threads. On pourra faire man pthread(3) pour avoir une liste exhaustive des fonctions utilisables pour les threads. La création de threads se fait avec la fonction pthread_create(3) qui prend notamment en argument un pointeur sur la fonction qu’exécutera le thread.

  1. Écrire un programme qui lance 8 nouveaux threads (en plus de celui du programme principal) sur une fonction void *start(void *) qu’on écrira. Cette fonction affichera le PID du processus. Le PID sera transmis à la fonction start par pthread_create par son dernier argument. N’oubliez pas de compiler avec l’argument CFLAGS+=-pthread.

  2. La fonction pthread_join(3) permet d’attendre un thread identifié par son THREAD ID (donné lors de l’appel de pthread_create. Modifiez le programme précédent pour que le thread principal attende tous les threads qu’il a créé puis affiche un message sur la sortie standard.

  3. Ajoutez deux variables entières au programme précédent, l’une globale, l’autre locale à la fonction start. Initialiser les à 0. Incrémentez dans start les deux variables, affichez leurs adresses et leurs valeurs. Que se passe-t-il ?

2. Vendeurs de tickets :

L'équipe Gala de l’Enseirb souhaite améliorer la façon dont elle vend les tickets pour la soirée. Actuellement, une seule personne est responsable de la vente des tickets (tickets.c) et cela nuit à sa bonne présence en cours et les attentes pour obtenir son billet sont bien souvent trop longues.

L'équipe Gala, pour gagner du temps, souhaite déléguer le travail à un pool de vendeurs de tickets pour paralléliser la vente. Modifier le programme tickets.c de façon à ce que le programme crée 8 étudiants (threads) qui s’occupent en parallèle de la vente des tickets. Le nombre de tickets à vendre restera une variable globale. Chaque étudiant/thread fera la boucle suivante:

  • Tester si le nombre de billets à vendre est positif
  • S’il est positif:
    • Faire un sched_yield(3)
    • Décrémenter de 1 le nombre de billets disponibles,
    • Incrémenter le nombre de billets vendus par ce thread.
  • Sinon
    • afficher le nombre de billets vendus par ce thread.
    • retourner le nombre de billets vendus par ce thread avec pthread_exit(3)

L'équipe Gala (le thread principal) devra récupérer le nombre de billets vendus par chacun des vendeurs et affichera le total de tickets vendus sur la sortie standard.

  • Expérimentez avec un nombre croissant de billets à vendre. Que se passe-t-il ?

  • Où est la section critique de ce code ?

  • Proposez deux modifications de ce programme permettant de mettre la section critique en exclusion mutuelle et de corriger le programme:

    1. À l’aide de sémaphore
    2. À l’aide de mutex
  • Vérifier qu’il n’y a plus le problème observé précédemment dans ces deux versions.

3. Producteurs et consommateurs

On souhaite améliorer le programme suivant (lecteurs.c). Ce programme possède deux fonctions pour un lecteur et un écrivain. Les deux partagent un tableau A commun de SIZE éléments.

  • Le producteur parcourt le tableau et en incrémente tous les éléments, affiche un message puis s’endort. Il effectue cette action LOOP fois puis termine normalement.
  • Le consommateur parcourt le tableau et calcule la somme de tous ses éléments et affiche un message. Affiche la somme en fin de boucle, puis recommence cette action LOOP fois. Il termine ensuite normalement.

Un consommateur ne peut lire une case que si le producteur l’a déjà remplie (ou modifée dans ce cas).

Actuellement, le programme proposé possède deux problèmes:

  1. le producteur doit avoir tout écrit avant que le consommateur ne puisse se mettre à lire.
  2. le producteur fait toutes les itérations avant que le lecteur ne commence. Celles-ci devraient s’entrelacer correctement.
  • Proposez une modification du programme de telle sorte que deux threads soient créés. Un thread aura le rôle de producteur, le deuxième le rôle du consommateur.

  • Faites en sorte de garantir l’exécution correcte du programme.

  • Attention, le lecteur et l'écrivain peuvent lire/écrire intégralement le tableau sans être interrompu par l’autre.

  1. Quelle est la section critique de ce code ?
  2. Tester le code avec un producteur et un consommateur puis mettre en place à l’aide de sémaphores une exclusion mutuelle sur la section critique.
  • On veut maintenant pouvoir lancer plus d’un thread consommateur. Plusieurs consommateurs peuvent accéder simultanément au tableau A. Cependant, il faut veiller à ce que chaque consommateur accède à une case différente des autres.

    1. Modifiez votre programme pour que 4 consommateurs soient lancés dans 4 threads et qu’un producteur soit lancé dans un thread. Notez que de nouvelles sections critiques peuvent apparaître en augmentant le nombre de consommateurs.

4. Diner des philosophes

8 philosophes s’assoient autour d’une table ronde pour dîner. Il y a entre chaque convive 1 baguette. Chaque philosophe a besoin de sa baguette de gauche et de celle de droite pour pouvoir commencer à dîner.

Écrire un programme philosophe.c qui lance 8 threads et qui simule ce banquet. L’utilisation de chaque baguette sera protégée par un mutex. La baguette étant disponible si le mutex n’est pas pris, et est utilisée par un autre philosophe si il est pris. On supposera également que les philosophes commencent à prendre leur baguette de droite avant de prendre celle de gauche. Chaque philosophe aura ainsi le comportement suivant:

  1. Réfléchi
void reflechi( int id ) {
	printf( "%2d: Je reflechi\n", id );
	usleep( random() % 2000 );
	printf( "%2d: J'ai faim\n", id );
}
  1. Prend les baguettes
  2. Mange
void mange( int id ) {
	printf( "%2d: Je mange\n", id );
	usleep( random() % 2000 );
	printf( "%2d: Je suis repu\n", id );
}
  1. Repose les baguettes

Chaque philosophe effectuera cette operation un nombre de fois assez grand pour révéler d'éventuels problèmes entre threads.

  • Que se passe-t-il ?
  • Proposer une modification du programme pour éviter ce problème.

Ressources additionnelles