| Chapitre 5 : Les mécanismes de synchronisation |
Le modèle de lecteurs/rédacteurs
Soit F est le nom du fichier partagé. Celui-ci est utilisable par F.Lire(X) et F.Ecrire(X) respectivement, pour lire un enregistrement et le récupérer dans X et pour écrire X dans un enregistrement.
Solution
- Demander_Lire
- Signaler_Fin_Lecture
- Demander_Ecrire
- Signaler_Fin_Ecriture
Il faut déterminer les conditions dans lesquelles les demandes mentionnées seront bloquantes ainsi que la signification des "Signaler_..".
Lecteur
While(1)
{
:
/* demander */
Si je suis le premier Alors attendre fichier libre ;
un lecteur de plus
F.lire(X)
/*signaler Fin lecture*/
un lecteur de moins;
Si je suis le dernier Alors libérer le fichier ;
:
:
}
Rédacteur
While(1)
{
:
:
:
:
/* demander */
Attendre fichier libre
F.Ecrire(X)
/*signaler Fin écriture*/
Libérer le fichier ;
:
:
:
}
Donc, nous avons d'un sémaphore, Fichier_Libre, pour illustrer cette barrière. L’attente se traduit alors par l’exécution de l’opération P sur ce sémaphore. La libération du fichier se traduit, quant à elle, par l’exécution de l’opération P sur ce même sémaphore.
Les lecteurs ont à utiliser un autre sémaphore, Mutex, pour pouvoir accéder en exclusion mutuelle au nombre courant de lecteurs.
La solution en termes de sémaphores de ce problème est illustrée par les algorithmes suivants:
Le problème de la voie unique
Enum direction {OUEST, EST}
On se donne comme objectif de résoudre ce problème de synchronisation dans les trois variantes suivantes :
Un seul véhicule à la fois sur la voie unique
Le comportement d’un véhicule est donné par l’algorithme suivant :
While(1)
{
Demander la voie unique ;
Accéder sur la voie unique ;
Libérer la voie unique ;
}
Sémaphore VU = 1 ;
While(1)
{
/*Demander la voie unique*/
P(VU) ;
Accéder sur la voie unique ;
/*Libérer la voie unique*/
V(VU) ;
}
Variante 2: Le nombre de véhicules à un instant donné sur la voie unique est illimité
While(1)
{
Demander la voie unique ;
Accéder sur la voie unique ;
Libérer la voie unique ;
}
La demande de la voie n’est pas bloquant si :
La voie est libre
Ou
Le sens-courant sur la voie correspond au sens du demandeur
Si aucune de ces 2 conditions n’est pas vérifiée alors on doit attendre.
Algorithme (pour un véhicule OUEST):
While(1)
{
/*Demander la voie unique*/
Si voie libre Alors sens_courant? OUEST ;
Si sens_courant ? OUEST Alors bloquer le demandeur ;
Sinon compter un véhicule de plus sur la voie unique ;
Traverser ;
/*Signaler fin utilisation de la voie*/
Signaler un véhicule de moins sur la voie
Si dernier véhicule sur la voie Alors il faut libérer les véhicules bloqués (contre-sens)
}
En termes de sémaphores, nous obtenons le comportement suivant :
Sémaphore Contre-Sens = 0 ;
Sémaphore MUTEX = 1 ;
Entier NB_ENGAGES, NB_BLOQUES = 0 ;
Direction Sens-Courant ;
Véhicule OUEST ;
Boolean Sens_Contraire = faux ; /*variable privé*/
While(1)
{
P(MUTEX)
Si NB_ENGAGES = 0 alors Sens_Courant = OUEST ;
Si Sens-Courant ? OUEST Alors
{
NB_BLOQUES++ ;
Sens-Contraire = Vrai ;
}
SINON NB_ENGAGES++ ;
V(MUTEX) ;
Si (Sens_Contraire) alors P(Contre_Sens) ;
Accés à la voie unique ;
P(MUTEX) ;
NB_ENGAGES-- ;
Si (NB_ENGAGES = 0) ET (NB_BLOQUES ? 0) Alors
{
Sens-Courant = EST ;
NB_ENGAGES = NB_BLOQUES ;
NB_BLOQUES = 0 ;
I =1 ;
TANT QUE (I = NB_ENGAGES) V(Contre-Sens) ;
}
V(MUTEX) ;
Contre exemple:
Sens_Courant == OUEST
P1 : Véhicule–EST, Il est interrompu après V(MUTEX) de la partie "demande"
et avant d’exécuter P(Contre-Sens) ;
P2 : Véhicule_OUEST, Il exécute la partie "libérer" en effectuant la
boucle TANT QUE …. V(Contre-Sens) ; il met un ticket de plus dans ce sémaphore.
P3 : Véhicule_OUEST (Sens_Courant == EST), Il va faire P(Contre_Sens)
mais sans se bloquer.
Sémaphore Contre-Sens[2] ={0, 0} ;
Sémaphore MUTEX = 1 ;
Entier NB_ENGAGES, NB_BLOQUES = 0 ;
Direction Sens-Courant ;
Véhicule OUEST ;
Boolean Sens_Contraire = faux ;
…
While(1)
{
MUTEX.P() ;
Si (NB_ENGAGES == 0) Alors Sens_Courant = OUEST ;
Si (Sens_Courant ? OUEST) Alors
{
NB_BLOQUES++ ;
Sens_Contraire = Vrai ;
}
SINON NB_ENGAGES++ ;
MUTEX.V() ;
Si (Sens_Contraire) Alors Contre_Sens[OUEST].P() ;
Accès à la voie unique ;
MUTEX.P() ;
NB_ENGAGES-- ;
Si (NB_ENGAGES = 0) ET (NB_BLOQUES ? 0) Alors
{
Sens_Courant = EST ;
NB_ENGAGES = NB_BLOQUES ;
NB_BLOQUES = 0 ;
I =1 ;
TANT QUE ( I = NB_ENGAGES)
{
Contre_Sens[EST].V() ;
I++ ;
}
}
MUTEX.V() ;
}
La capacité de la voie est limitée à N véhicules
Un véhicule est non admis sur la voie, si :
Sens_Courant ? Sens_Véhicule
ou
(Sens_Courant == Sens_Véhicule) ET (NB_ENGAGES ==N)
/*Signaler fin utilisation de la voie*/
S’il existe de véhicules de même sens que moi bloqué ALORS réveiller un de ces
véhicules.
SINON Signaler un véhicule de moins sur la voie
SI je suis le dernier véhicule ALORS réveiller au maximum N véhicules de l’autre sens
Sémaphore Véhicules-Bloqués[2] ={0, 0} ;
Sémaphore MUTEX = 1 ;
Entier NB_ENGAGES = 0 ;
Entier NB_BLOQUES[2] = {0,0} ;
Direction Sens-Courant ;
Véhicule OUEST ;
Boolean Non_Admis = faux ;
…
While(1)
{
MUTEX.P() ;
SI (NB_ENGAGES == 0) Alors Sens_Courant = OUEST ;
SI (Sens_Courant ? OUEST) OU (NB-ENGAGES == N) Alors
{
NB_BLOQUES[OUEST]++ ;
Non_Admis = Vrai ;
}
SINON NB_ENGAGES++ ;
MUTEX.V() ;
Si (Non_Admis) Alors Véhicules_Bloqués[OUEST].P() ;
Accès à la voie unique ;
MUTEX.P() ;
NB_ENGAGES-- ;
SI (NB_BLOQUES[OUEST] ? 0) Alors
{
NB_BLOQUES[OUEST]-- ;
Véhicules-Bloqués[OUEST].V() ;
}
SINON
{
NB_ENGAGES--;
SI (NB_ENGAGES == 0) ET (NB_BLOQUES[EST]) Alors
{
Sens_Courant = EST ;
NB_ENGAGES = min(NB_BLOQUES[EST], N) ;
NB_BLOQUES[EST] -= NB_ENGAGES ;
I =1 ;
TANTQUE(I = NB_ENGAGES)
{
Contre-Sens[EST].V() ;
I++;
}
}
}
MUTEX.V() ;
}
Il faut comptabiliser les véhicules engagés dans chaque sens. Donc, il faut utiliser 2 compteurs. Chaque compteur est accédé en Exclusion mutuelle par les éléments de sa classe.
Le même sémaphore est utilisé pour les deux rôles :
Sémaphore Voie_Unique = 1 ;
Sémaphore MUTEX[2] = {1,1} ;
Entier NB_ENGAGES[2] == {0,0} ;
Direction Sens_Courant ;
Véhicule OUEST ;
/*Demander la voie*/
While(1)
{
MUTEX[OUEST].P()
SI (NB_ENGAGES [OUEST] == 0) Alors
{
Voie_Unique.P() ;
Sens_Courant = OUEST ;
}
NB_ENGAGES++ ;
MUTEX[OUEST].V() ;
Accés à la voie unique ;
/*Libérer la voie*/
MUTEX[OUEST].P() ;
NB_ENGAGES[OUEST]-- ;
SI (NB_ENGAGES[OUEST] == 0) Alors Voie_Unique.V() ;
MUTEX[OUEST].V() ;
}