|
|
Institut National des
Télécommunications
Télécom INT 2è
année
TP Noté CSC4508/M2 du 25/05/07
(Corrigés)
Modalités
Durée : 3 heures
Tous documents autorisés.
Les questions sont indépendantes les unes
des autres. Aussi, n'hésitez pas à lire tout le
sujet avant de commencer pour déterminer l'ordre dans lequel
vous souhaitez traiter les questions.
Le barême est donné à titre indicatif
des poids
entre les différentes questions.
La « livraison » de votre travail
en fin de TP
noté se fera par remise de votre copie à
l'enseignant et
par
remontée sous Moodle (rubrique « TP
noté de 3
heures ») du fichier d'extension tgz
constitué de
la manière suivante :
cd votreRepertoireDeTravailPourCSC4508M2 tar cvfz $USER.tgz TPNote2007
Préparation
cd votreRepertoireDeTravailPourCSC4508M2 cp ~simatic/Cours/CSC4508/tPNote2007.tgz . tar xvfz tPNote2007.tgz cd TPNote2007
Question 1 : Files d'attente de
sémaphore et priorité de processus (4 points)
Pour chacune des questions ci-dessous, répondez sur votre
copie
ou
bien dans le fichier Q1/reponse1.txt (dans ce
dernier
cas, indiquez sur votre
copie « Réponse dans Q1/reponse1.txt »
).
Chaque objet système sémaphore comprend une
file d'attente des
processus
qui ont fait une opération P() et
attendent qu'un autre
processus fasse une opération V().
À l'aide de deux expériences, cet exercice
cherche à prouver que cette file d'attente
ne prend en compte ni les priorités statiques, ni
les priorités dynamiques des processus
qui sont en attente sur ce sémaphore. Dit autrement, en cas
d'opération V(), ce
n'est pas le processus en attente le plus prioritaire (statiquement ou
dynamiquement) qui
est réveillé en premier.
Question 1.1 : Expérience avec des
processus à
priorités statiques différentes (2 points)
On décide de
s'appuyer sur le code du fichier Q1/testSemaphoreEtPrioriteStatique.c
listé ci-dessous :
/*************************************/
/*
testSemaphoreEtPrioriteStatique.c */
/*************************************/
#include
<stdlib.h>
#include
<unistd.h>
#include
<stdio.h>
#include
<sched.h>
#include
<sys/types.h>
#include
<sys/wait.h>
#include
<sys/ipc.h>
#include
<sys/sem.h>
#define
FATAL(msg) {\
char
m[128]; \
sprintf(m, "%s in %s:%d:%s", msg, __FILE__, __LINE__, __func__); \
perror(m); \
abort(); \
}
/**********************************************************************/
/* Code de P
et
V
*/
/**********************************************************************/
void P(int
semId){
struct sembuf op;
op.sem_num = 0;
op.sem_op = -1;
op.sem_flg = 0;
if
(semop(semId, &op, 1) == -1){
FATAL("semop");
}
}
void V(int
semId){
struct sembuf op = {0, 1, 0};
if
(semop(semId, &op, 1) == -1){
FATAL("semop");
}
}
/**********************************************************************/
/* Code
execute par les processus
enfant
*/
/**********************************************************************/
void
codeEnfant(int numEnfant, int semId)
{
struct sched_param sp;
/*
Section de Code 1 (SC1) */
sp.sched_priority = numEnfant;
if
(sched_setscheduler(0,SCHED_FIFO,&sp) < 0) {
FATAL("sched_setscheduler");
}
/*
Section de Code 2 (SC2) */
sleep(numEnfant);
/*
Section de Code 3 (SC3) */
P(semId);
/*
Section de Code 4 (SC4) */
printf("Enfant %d s'est reveille !\n", numEnfant);
exit(EXIT_SUCCESS);
}
/**********************************************************************/
/* Programme
principal
*/
/**********************************************************************/
int main(int
argc, char* argv[])
{
int
status;
key_t cle;
int
i;
int
semId;
/*
Section de Code 5 (SC5) */
cle
= ftok("./testSemaphoreEtPrioriteStatique.c", '0');
if
(cle < 0) {
FATAL("ftok");
}
semId = semget(cle, 1, IPC_CREAT|0666);
if
(semId < 0) {
FATAL("semget");
}
if
(semctl(semId, 0, SETVAL, 0) < 0) {
FATAL("semctl");
}
/*
Section de Code 6 (SC6) */
for
(i=1 ; i<4 ; i++){
switch (fork()) {
case -1:
FATAL("fork");
break;
case 0:
codeEnfant(i, semId);
break;
default:
break;
}
}
/*
Section de Code 7 (SC7) */
sleep(4);
/*
Section de Code 8 (SC8) */
for
(i=1 ; i<4 ; i++){
V(semId);
if (wait(&status) < 0){
FATAL("wait");
}
}
/*
Section de Code 9 (SC9) */
if
(semctl(semId, 0, IPC_RMID, 0) < 0){
FATAL("semctl");
}
return EXIT_SUCCESS;
}
Décrivez l'expérience
réalisée avec ce code.
---beginCorr
Barême : 1,5 points
Dans ce code, un processus parent crée un
sémaphore qu'il
initialise à 0 (Section de Code 5). Puis il forke 3 enfants
(SC6), l'enfant 1 (respectivement 2 et 3) ayant une priorité
statique de 1 (respectivement 2 et 3). Les enfants dorment avec des
durées différentes de manière
à garantir
que les processus font l'opération P() dans
l'ordre
inverse de
leur priorités (ainsi le processus le moins prioritaire est
le
premier à faire l'opération P()).
Le processus parent dort suffisamment pour qu'on soit sûr que
ses
3 enfants ont fait l'opération P() (SC7). Il
fait ensuite 3
opérations V()
(SC7) :
- Si la file d'attente tient compte des
priorités statiques, on devrait avoir l'affichage :
Enfant 3
s'est reveille !
Enfant 2
s'est reveille !
Enfant 1
s'est reveille !
- Sinon on aura l'affichage :
Enfant 1
s'est reveille !
Enfant 2
s'est reveille !
Enfant 3
s'est reveille !
---endCorr
A l'exécution, on obtient l'affichage
suivant :
Enfant 1 s'est reveille !
Enfant 2 s'est
reveille !
Enfant 3 s'est
reveille !
Qu'en concluez-vous ?
---beginCorr
Barême : 0,5 point
La file d'attente du sémaphore ne tient donc pas compte des
priorités statiques.
---endCorr
Question 1.2 : Expérience avec des
processus à
priorités dynamiques différentes (2 points)
Modifiez le fichier Q1/testSemaphoreEtPrioriteDynamique.c (qui,
pour l'instant, est une copie conforme de Q1/testSemaphoreEtPrioriteStatique.c )
pour adapter l'expérience de la question 1.1 au cas de
processus avec des priorités dynamiques.
---beginCorr
Barême : 1,5 point (0,75 points par modification)
Le code doit être modifié en tenant compte du fait
qu'avec
les priorités dynamiques, ce sont les valeurs hautes de
priorité qui sont les moins prioritaires (alors que pour les
priorités statiques, c'est le contraires).
Le code suivant de Q1/testSemaphoreEtPrioriteStatique.c
/* Section de Code
1 (SC1)
*/
sp.sched_priority = numEnfant;
if
(sched_setscheduler(0,SCHED_FIFO,&sp) < 0) {
FATAL("sched_setscheduler");
}
/* Section
de Code 1 (SC2)
*/
sleep(numEnfant);
doit donc être remplacé par
/* Section de Code
1 (SC1)
*/
if (nice(numEnfant) <
0) {
FATAL("nice");
}
/*
SC2 (noter le "numEnfant" qui est devenu un "4-numEnfant" pour qu'on
soit sur que le */
/* processus le moins prioritaire (donc le processus de
priorite 3) soit celui qui fait */
/* l'operation P() en premier.
*/
sleep(4-numEnfant);
---endCorr
Compilez Q1/testSemaphoreEtPrioriteDynamique.c
Exécutez Q1/testSemaphoreEtPrioriteDynamique
Sur
votre copie ou bien dans le fichier Q1/reponse1.txt ,
indiquez l'affichage obtenu et votre conclusion.
---beginCorr
Barême : 0,5 point
On a l'affichage :
Enfant 3 s'est reveille !
Enfant 2 s'est
reveille !
Enfant 1 s'est
reveille !
La file d'attente du sémaphore ne tient donc pas compte des
priorités dynamiques.
---endCorr
Question 2 : Des threads pour un moteur (4
points)
Pour chacune des questions ci-dessous, répondez sur votre
copie
ou
bien dans le fichier Q2/reponse2.txt (dans ce
dernier
cas, indiquez sur votre
copie « Réponse dans Q2/reponse2.txt »).
Le but du programme Q2/indexation.c
est de
créer un moteur d'indexation parcourant une arborescence de
documents. Voici son listing :
#include
<stdio.h>
#include
<stdlib.h>
#include
<pthread.h>
#include
<sys/types.h>
#include
<dirent.h>
#include
<assert.h>
#include
<unistd.h>
#include
<string.h>
#define
NBMAXDOCS 20
#define
MAXDIRNAME 80
int
nbDocIndexes = 0;
int nbThreads
= 0;
char
docname[MAXDIRNAME];
char
dirname[MAXDIRNAME];
void
*indexer_document(void *arg)
{
char *docname = (char *)arg;
printf("Debut indexer document =%s\n", docname);
sleep(3);
printf("Fin indexer document =%s\n", docname);
nbDocIndexes ++;
pthread_exit(NULL);
}
void
indexer_repertoire(DIR *dir)
{
struct dirent *entry;
pthread_t thread;
int
rc, i = 0;
do {
entry = readdir(dir);
if (entry == NULL)
break;
strcpy(docname, entry->d_name);
nbThreads ++;
rc = pthread_create(&thread, NULL, indexer_document, docname);
if (rc){
perror("pthread_create");
exit(EXIT_FAILURE);
}
i++;
}
while (i < NBMAXDOCS);
if
(i == NBMAXDOCS){
fprintf(stderr, "Indexation incomplete: le nombre de documents est
superieur a NBMAXDOCS (%d)\n", NBMAXDOCS);
}
}
int main(int
argc, char **argv){
DIR
*directory;
int
i;
if
(argc < 2){
fprintf(stderr, "Usage: %s listeRepertoires\n", argv[0]);
exit(EXIT_FAILURE);
}
for
(i = 1; i < argc; i++){
assert(strlen(argv[i]) < MAXDIRNAME);
strcpy(dirname, argv[i]);
directory = opendir(dirname);
if (directory == NULL){
perror("opendir");
exit(EXIT_FAILURE);
}
indexer_repertoire(directory);
closedir(directory);
}
printf("Le nombre de threads crees est = %d\n", nbThreads);
printf("Le nombre de documents indexes est = %d\n", nbDocIndexes);
exit(EXIT_SUCCESS);
}
Question 2.1 : Analyse du code (2 points)
- Combien de
threads sont
créés?
---beginCorr
Barême : 0,5 point
Autant de threads que de documents à indexer (avec un
maximum à NBMAXDOCS)
---endCorr
- Quel est le rôle de chaque
thread ?
---beginCorr
Barême : 0,5 point
Le thread principal parcourt les répertoires
donnés en entrée et crée
un thread pour chacune des entrées des
répertoires. Chaque thread créé
par le thread principal est en charge de l'indexation d'un document.
---endCorr
- Combien de
threads font
appel à la fonction readdir() ?
---beginCorr
Barême : 0,5 point
Uniquement le thread principal fait appel à readdir .
---endCorr
- Précisez pour chacune des variables
globales si ces variables sont accédées par un ou
plusieurs
threads . Expliquez.
---beginCorr
Barême : 0,5 points
nbDocIndexes :
accès concurrent en écriture par tous les
threads indexeurs
nbThreads :
accès par le thread principal uniquement
docname : accès
concurrent en lecture par les threads indexeurs et en
écriture par le thread principal
dirname : accès
par le thread principal uniquement
---endCorr
Question 2.2 : Débuggage du code
Ce code ne s'exécute pas correctement. Il contient 4
erreurs. Donnez
chacune des erreurs et proposez un principe de correction (sur votre
copie ou dans
le fichier Q2/reponse2.txt
; NB : ne modifiez pas le fichier Q2/indexation.c
: il ne sera pas corrigé !).
---beginCorr
Barême : 0,5 point par item
- Le thread principal fait appel à
exit
et termine donc tous les threads même s'ils n'ont pas fini
l'indexation: remplacer par un appel à pthread_exit()
- Tous les threads accèdent à la
zone mémoire
docname
écrasée par le thread principal: créer
une zone pour chaque thread
- Tous les threads indexeurs accèdent en
écriture de manière concurrente à
nbDocIndexes
: protéger l'accès avec un mutex
- Le thread principal doit attendre la fin des autres
threads avant l'affichage: faire un
join sur
chaque thread
Voici le fichier corrigé : indexation.corrige.c
---endCorr
Question 3 : BoursINT (garanti sans ail, ni fines
herbes ;-) ) (12 points)
Pour chacune des questions ci-dessous, répondez sur votre
copie
ou
bien dans le fichier Q3/reponse3.txt (dans ce
dernier
cas, indiquez sur votre
copie « Réponse dans Q3/reponse3.txt »).
BoursINT, le club Bourse de l'INT, a décidé
d'automatiser
sa surveillance des différentes salles de marché
avec lesquelles il travaille.
L'idée est d'avoir une application constituée de
N+1 tâches et d'un tableau t de N doubles
partagé entre les
N+1 tâches :
- N tâches (nommées Si
[comme
Surveillant] dans la suite) sont chargées de surveiller
chacune
une salle de marché (Paris, Londres, New York...). Chaque
tâche Si
calcule plus ou moins rapidement le résultat de la
séance (sous
la
forme d'un nombre réel indiquant la tendance -en pourcentage
de hausse
ou de
baisse- des valeurs dans cette salle). Une fois son calcul
effectué, elle écrit ce
résultat dans la iè
case du tableau t.
- Quand les N tâches de surveillance ont
chacune
écrit leur case de tableau,
elles préviennent la N+1è
tâche
(nommée A [comme Afficheur] dans la suite) qu'elle doit
s'exécuter pour afficher la moyenne des valeurs du tableau t.
Les tâches Si se mettent
alors en attente jusqu'à ce que la tâche A leur
indique qu'elle a
terminé son calcul de moyenne. Elles redémarrent
alors leur surveillance.
Question 3.1 : Identification des paradigmes
(1,5
point)
Indiquez les différents types de problème de
synchronisation auxquels vous êtes
confrontés pour
implémenter
cette application (exclusion mutuelle, cohorte,
producteur/consommateur...) en précisant, pour chaque
problème,
ses acteurs.
---beginCorr
Barême : 0,5 point par paradigme
- 2 paradigmes de type signal
- Groupe des tâches Si
/ Tâche A (quand tous les membres du groupe des
tâches Si ont écrit dans
leur case de tableau,
ils signalent à la tâche A qu'elle peut
démarrer son calcul de moyenne. NB : on pourrait dire aussi
qu'il y a un paradigme de type signal entre chaque Si et
la tâche A
- Tâche A / groupe des
tâches Si
- Paradigme de type Exclusion Mututelle
- Pour déclencher le signal entre le
groupe des tâches Si et la
tâche A, on aura besoin de compter le nombre de
tâches Si qui ont écrit
leur donnée dans le tableau t. On a donc
besoin d'une exclusion mutuelle entre ces tâches pour
protéger ce compteur.
---endCorr
Question 3.2 : Conception à l'aide de
sémaphores (4
points)
Le club BoursINT a commencé à écrire
les algorithmes :
Variables globales
==================
// Tableau permettant la communication entre les tâches
// Surveillant et la tâche Afficheur
t Tableau[1..N] de doubles
// Variables associées aux paradigmes de type Signal
Sémaphore topAfficheur initialisé à ? // A completer
Sémaphore topSurveillant initialisé à ? // A completer
// A completer (eventuellement)
Tache Surveillanti
==================
faire
t[i]=surveiller(i)
// A completer
tant que VRAI
Tache Afficheur
===============
faire
P(topAfficheur)
Ecrire "Moyenne = ", moyenne(t)
pour i=1:Entier à N faire
V(topSurveillant)
fpour i
tant que VRAI
Compléter les déclarations de variables globales et le(s)
algorithme(s) (en particulier, tous les commentaires « // A completer » doivent disparaître) pour répondre au problème posé.
NB :
- Si jamais vous avez besoin d'autres sémaphores,
essayez de vous limiter à un
seul sémaphore supplémentaire.
- Vous pouvez modifier l'algorithme de la tâche Afficheur si vous en éprouvez le besoin.
- Sous sa forme actuelle, l'algorithme de la tâche Afficheur
comprend une anomalie si la file d'attente associée aux
sémaphores n'est pas FIFO. Ne vous préoccupez pas de
cette anomalie dans cette question (elle sera traitée à
la question 3.4)
---beginCorr
Barême :
- Toutes les variables sont là : 0,5 (-
0,125
par
variable manquante sachant qu'on ne compte pas la
déclaration du tableau)
- Toutes les variables sont correctement
initialisées
: 0,5 (-0,125 par variable incorrectement initialisée)
- 1,5 points par paradigme
signal correctement implémenté (il y en a 2)
- 1 point pour la gestion correcte de la variable nbSurveillantTermine (0,5 pour l'incrément de 1 et 0,5 pour la remise à zéro).
- 0,5 point pour exclusion mutuelle sur nbSurveillantTermine correctement
implémentée
Variables globales ================== // Tableau permettant la communication entre les tâches // Surveillant et la tâche Afficheur t Tableau[1..N] de doubles // Variables associées aux paradigmes de type Signal Sémaphore topAfficheur initialisé à 0 Sémaphore topSurveillant initialisé à 0 // Variable pour compter le nombre de surveillant ayant terminé Entier nbSurveillantTermine initialisé à 0 // Variables associées au paradigme de type Exclusion Mutuelle Sémaphore mutexNbSurveillantTermine initialisé à 1
Tache Surveillanti ================== faire t[i]=surveiller(i) P(mutexNbSurveillantTermine) nbSurveillantTermine += 1 si nbSurveillantTermine == N alors V(topAfficheur) nbSurveillantTermine = 0 finsi V(mutexNbSurveillantTermine) P(topSurveillant) tant que VRAI
Tache Afficheur =============== faire P(topAfficheur) Ecrire "Moyenne = ", moyenne(t) pour i=1:Entier à N faire V(topSurveillant) fpour i tant que VRAI
---endCorr
Question 3.3 : Codage (5 points)
Dans le fichier Q3/boursINT.c,
implémentez les algorithmes de la question 3.2, en
prenant N
égal à 9 et en utilisant des processus enfants.
Pour les tâches Si, la
fonction surveiller
est fournie dans le fichier (son résultat est à
stocker dans la case concernée du tableau t).
Seront
notés pour cette question :
- Le fait que, à l'exécution, le
code répond correctement
au problème posé,
- La clarté du code,
- Le fait que le code compile (sans warning),
- Le fait que le retour de chaque appel
système, s'il
y en
a un, est testé et géré (traitement
d'erreur),
- Le fait que les ressources système
réservées pendant l'exécution du
programme
sont libérées à la fin de
l'exécution.
---beginCorr
Barême : 0,75 point par rubrique "Seront notés"
ci-dessus, sauf pour l'exécution correcte qui vaut 2 points (NB
: 1,5 points sont retirés si le codage est fait avec des threads, vu qu'on demande explicitement que ce soit fait avec des enfants).
Voir boursINT.corrige.c
---endCorr
Question 3.4 : Correction de l'anomalie de la question
3.2 (1,5
points)
Les algorithmes conçus à la question 3.2 ne
fonctionnent
correctement que si on suppose que les sémaphores ont une
liste
d'attente associée de type FIFO.
Donnez un exemple de ce qui pourrait arriver avec l'algorithme de la
question 3.2 si la liste d'attente n'était pas FIFO.
Expliquez notamment le dysfonctionnement résultant.
En utilisant au plus 4 sémaphores,
écrivez des
algorithmes qui fonctionnent même si les listes d'attente
associées au sémaphore ne sont pas FIFO.
---beginCorr
Barême :
- 0,5 point pour l'explication
- 0,5 point pour le sémaphore
supplémentaire et son initialisation
- 0,5 point pour le fonctionnement de l'algorithme
(difficile d'établir un barême précis, compte tenu
du fait que d'autres solutions que celle
proposée dans le corrigé).
Que pourrait-il arriver si la liste d'attente n'est pas FIFO ?
- Le jour 0, toutes les tâches Surveillant
s'exécutent et se bloquent sur le P(topSurveillant),
la dernière débloquant la tâche
Afficheur.
- La tâche Afficheur fait N V(topSurveillant).
Imaginons que la tâche S1
s'exécute et qu'elle finit tout de suite son
travail. S1
va alors faire un nouveau P(topSurveillant).
- Si la file d'attente n'est pas FIFO, S1
peut
très bien ne pas être bloqué par ce P() (si jamais
les autres tâches Si ne se
sont pas encore réveillées).
- A l'extrème, S1
peut
s'exécuter N fois sans que les autres tâches ne se
soient jamais réveillées !
- Donc, quand elle va débloquer la
tâche Afficheur, cette dernière affichera une
moyenne fausse (puisque prenant en compte seulement les
mises-à-jour de la tâche S1).
Variables globales ================== // Tableau permettant la communication entre les tâches // Surveillant et la tâche Afficheur t Tableau[1..N] de doubles // Variables associées aux paradigmes de type Signal Sémaphore topAfficheur initialisé à 0 Sémaphore topSurveillantJourPair initialisé à 0 Sémaphore topSurveillantJourImpair initialisé à 0 // Variable pour compter le nombre de surveillant ayant terminé Entier nbSurveillantTermine initialisé à 0 // Variables associées au paradigme de type Exclusion Mutuelle Sémaphore mutexNbSurveillantTermine initialisé à 1
Tache Surveillanti ================== Entier numJour initialisé à 0 faire t[i]=surveiller(i) P(mutexNbSurveillantTermine) nbSurveillantTermine += 1 si nbSurveillantTermine == N alors V(topAfficheur) nbSurveillantTermine = 0 finsi V(mutexNbSurveillantTermine) numJour += 1 si numJour modulo 2 == 0 alors P(topSurveillantJourPair) sinon P(topSurveillantJourImpair) finsi tant que VRAI
Tache Afficheur =============== Entier numJour initialisé à 0 faire P(topAfficheur) Ecrire "Moyenne = ", moyenne(t)
numJour += 1 pour i=1:Entier à N faire si numJour modulo 2 == 0 alors V(topSurveillantJourPair) sinon V(topSurveillantJourImpair) finsi fpour i tant que VRAI
---endCorr
Question (bonus) 3.5 : Reprise de la question 3.4 avec des objets de synchronisation threads (bonus de 2
points)
Reprendre le problème de la question 3.4, à
l'aide d'objets de synchronisation thread
(mutex avec les opérations pthread_mutex_lock
et pthread_mutex_unlock
et conditions avec les opérations pthread_cond_wait ,
pthread_cond_signal
et éventuellement pthread_cond_broadcast ).
---beginCorr
Barême :
- 0,5 point pour les objets de synchronisation (-0,125
point par objet manquant)
- 0,5 point pour la variable permettant de déterminer quel cycle doivent traiter les tâches Si
- 1 point pour le fonctionnement de l'algorithme
(difficile d'établir un barême précis, compte tenu
du fait que d'autres solutions que celle
proposée dans le corrigé).
Variables globales ================== // Tableau permettant la communication entre les tâches // Surveillant et la tâche Afficheur t Tableau[1..N] de doubles // Numéro du prochain jour pour lequel Afficheur doit // afficher la moyenne Entier numJourTraiteActuellement initialisé à 0 // Variable pour compter le nombre de surveillant ayant terminé Entier nbSurveillantTermine initialisé à 0 // Mutex et conditions nécessaires pour l'algorithme pthread_mutex_t mutexNbSurveillantTermine; pthread_mutex_t mutexAffichage; pthread_mutex_t mutexNumJourTraiteActuellement; pthread_cond_t condNumJour;
Tache Surveillanti ================== Entier numJourCourant initialisé à 0 faire t[i]=surveiller(i) pthread_mutex_lock(&mutexNbSurveillantTermine) nbSurveillantTermine += 1 si nbSurveillantTermine == N alors pthread_mutex_unlock(&mutexAffichage) nbSurveillantTermine = 0 finsi pthread_mutex_unlock(&mutexNbSurveillantTermine)
numJourCourant += 1 tant que (numJourTraiteActuellement < numJourCourant) faire pthread_cond_wait(&condNumJour, &mutexNumJourTraiteActuellement); fintantque tant que VRAI
Tache Afficheur =============== faire pthread_mutex_lock(&mutexAffichage) Ecrire "Moyenne = ", moyenne(t)
pthread_mutex_lock(mutexNumJourTraiteActuellement); // NB : ce pthread_mutex_lock est a priori inutile, car // on est sûr qu'a cet instant, la tache Afficheur est seul à accéder // à cette variable, car tous les surveillants // sont en attente du pthread_cond_broadcast qui va suivre // Donc, pas besoin de faire une exclusion mutuelle en utilisant // mutexNumJourTraiteActuellement // Comme c'est une optimisation pas vraiment pédagogique, on laisse // le pthread_mutex_lock numJourTraiteActuellement += 1 pthread_mutex_unlock(mutexNumJourTraiteActuellement); pthread_cond_broadcast(&condNumJour) tant que VRAI
---endCorr
|