TP 3 : threads et sémaphores
🎯 Objectifs
- Découvrir la programmation concurrente avec les threads
🔧 Matériel nécessaire
- Ordinateur avec compilateur gcc
- CodeBlocks/VS Code
Partie 1 : les sémaphores
Rappels
Un sémaphore est une variable entière qui permet de contrôler l’accès à une ressource partagée par plusieurs threads. Il peut prendre des valeurs positives ou nulles.
Si le sémaphore est supérieur à 0, cela signifie que la ressource est disponible et qu’un thread peut y accéder. Si le sémaphore est égal à 0, cela signifie que la ressource est occupée et qu’un thread doit attendre avant de pouvoir y accéder.
Un sémaphore est généralement manipulé à l’aide de deux opérations atomiques :
wait()(ouP()), qui décrémente la valeur du sémaphore. Si la valeur devient négative, le thread est bloqué jusqu’à ce que le sémaphore soit disponible.signal()(ouV()), qui incrémente la valeur du sémaphore. Si la valeur était négative, un thread bloqué est réveillé.
Exercice
Voici deux processus partageant deux ressources (A et B) :
// Processus 1
P(A);
V(A);
P(B);
V(B);
// Processus 2
P(A);
V(A);
P(B);
V(B);
Les conditions initiales sont les suivantes :
// Initialisation des sémaphores
a = 1;
b = 0;
Q1. Montrez que les processus entrent systématiquement en interblocage.
Q2. Proposez une modification des conditions initiales pour éviter l’interblocage.
Q3. Montrez graphiquement que votre solution fonctionne.
Partie 2 : l’étoile mouvante
Nous allons, dans cette partie, programmer un simple jeu d’étoile qui se déplace dans un plateau. Lorsqu’elle rencontre un obstacle, elle rebondit dans le sens opposé. Il est aussi possible de diriger l’étoile avec les touches ZQSD du clavier.
Q1. Récupérez l’archive jeu.zip sur Arche et importez les fichiers dans un nouveau projet CodeBlocks.
L’archive contient les fichiers suivants :
jeu.c: le fichier principal du programmejeu.h: le fichier d’en-têteutilitaires.c: des fonctions utilitaires
Le fichier jeu.c contient la fonction main() qui effectue les opérations suivantes :
- lecture du fichier
plateau.txtqui contient la configuration du plateau de jeu - placement de l’étoile à une position aléatoire
- lancement de deux threads :
- un thread
animqui gère l’animation de l’étoile - un thread
clavierqui gère les entrées clavier
- un thread
On stocke le plateau grâce à un tableau d’entiers qui doit respecter ce format :
- 0 : case vide
- 1 : obstacle
- 2 : étoile
Warning
Les codes sont utilisables sous linux par défaut mais il suffira de décommenter les lignes spécifiques à Windows si vous êtes sous cet OS. En particulier, la fonction sleep() sous linux est remplacée par Sleep() sous Windows via la bibliothèque windows.h.
Q2. Dans le fichier utilitaires.c, complétez la fonction afficher_plateau() afin d’afficher le plateau de jeu dans la console. Il faudra penser à effacer la console avant chaque affichage.
Q3. Écrivez la fonction placer_star() qui place l’étoile à une position aléatoire dans le plateau.
Note
Vous pourrez utiliser vos codes sur la génération de nombres aléatoires vus en TP1.
Warning
Il faudra penser à vérifier que la position choisie n’est pas un obstacle.
Q4. Écrivez la fonction calculer_direction() qui met à jour la direction que doit prendre l’étoile (star->direction). Pour rappel, l’étoile change de direction lorsqu’elle rencontre un obstacle.
Q5. Écrivez la fonction deplacer_star() qui est appelée par le thread anim. Après avoir fait appel à la fonction calculer_direction(), cette fonction met à jour le plateau puis l’affiche avant de s’endormir pendant une seconde.
Gestion de la vitesse
Q6. Ajoutez un menu au lancement du programme permettant à l’utilisateur de choisir la vitesse de l’étoile.
| Vitesse | Délai |
|---|---|
| 1 - Lent | 1000 ms |
| 2 - Normal | 500 ms |
| 3 - Rapide | 200 ms |
Vous pourrez utiliser la fonction suivante :
// Millisecond sleep
void msleep(int milliseconds) {
struct timespec ts;
ts.tv_sec = milliseconds / 1000;
ts.tv_nsec = (milliseconds % 1000) * 1000000;
nanosleep(&ts, NULL);
}