Objet protégé :: exemple
type Valeur_Registre is array(1..Taille_Registre) of Integer;
protected Le_Registre is
function Lire return Valeur_Registre; procedure Incremente;
private
La_Valeur : Valeur_Registre := (others => 0); end Le_Registre ;
protected body Le_Registre is
function Lire return Valeur_Registre is begin
return La_Valeur ;
end Lire;
procedure Incremente is
begin
for I in La_Valeur ‘range loop
La_Valeur(I) := La_Valeur(I) + 1;
end loop;
end Incremente;
end Le_Registre ;
Partie spécification
Partie implémentation (body)
Objetbjet protégéprotégé :: exemexempleple (suite)(suite)
task type T_F;
task body T_F is
begin
for I in 1..1_000_000 loop
Le_Registre.Incremente;
end loop;
end T_F;
R_Local : Valeur_Registre;
begin — début du main
declare
T1, T2: T_F; — déclaration de deux tâches de type T_F begin
null; — execution de t1 et de t2 en concurrence; on attend la fin end;
Put_line(« Valeur finale du registre « );
R_Local := Le_Registre.Lire;
for I in R_Local’Range loop
Put_line(Integer’Image( R_Local(I) ));
end loop;
end Prog_Exc;
Objetbjet protégéprotégé :: exemexempleple (suite)(suite)
Voici un exemple d’exécution
Valeur finale du registre
2000000
2000000
2000000
2000000
2000000
2000000
2000000
2000000
2000000
2000000
On a bien 10 lignes avec la valeur 2000000 (deux millions)
-> les séquences lecture / incrémentation / écriture ne sont plus entrelacées : les incrémentations peuvent être considérées comme atomiques au niveau de l’objet protégé; ceci est dû à la sémantique d’accès exclusif des objets protégés
Objet protégé :: sémantiqueique des entrées
n Une entrée permet de définir des traitements sous conditions
– Chaque entrée possède une file d’attente à laquelle est associée une expression booléenne que l’on appelle « garde » ou « barrière »
– L’entrée n’est « ouverte » que si cette expression (la garde) est vraie
– Lorsque l’entrée est « fermée » (la garde est fausse), les appels sur cette entrée sont mis en attente (ils ne seront traités que lorsque la garde redeviendra vraie)
n Une tâche, exécutant le code d’une entrée, peut être placée dans la file d’attente d’une autre entrée par l’instruction « requeue »
n Une tâche en attente sur une entrée interne est plus prioritaire qu’une tâche faisant un « nouvel » appel à une entrée (ou procédure) de l’objet protégé.
n Supposons que le registre étudié précédemment
– corresponde à des paramètres de vol d’un avion et que la première case de ce registre indique si l’avion monte (valeur positive) ou s’il descend (valeur négative).
– qu’il puisse être modifié par incrémentation ou décrémentation.
– qu’il soit utilisé par une tâche qui ne gère que la descente de l’avion
n Une première possibilité est de faire faire une boucle d’attente à la tâche
– lecture du registre;
– vérification si le sens est « montée » ou « descente »;
– si le sens est « montée »
• attendre en certain temps et reboucler
Cette technique est inefficace (consommation cpu) et complique inutilement le code de la tâche
n Une seconde possibilité est d’utiliser une entrée d’un objet protégé afin de mettre sous conditions la lecture : dans notre exemple, la lecture ne sera possible que si la valeur de la première case du registre est négative
n Ceci s’implémente simplement de la façon suivante (la partie qui ne change pas par
rapport à la solution précédente est grisée)
protected Le_Registre is
function Lire return Valeur_Registre; procedure Incremente; procedure Decremente;
entry Lire_Sous_Conditions(V: out Valeur_Registre); private
La_Valeur : Valeur_Registre := (others => 0); end Le_Registre ;
déclaration de l’entrée
EExemplexemple d’utilisationd’utilisation desdes entréesentrées (suite)(suite)
n Le corps de l’entrée utilise une garde qui correspond à la condition de progression
protected body Le_Registre is
function Lire return Valeur_Registre is
begin
return La_Valeur ;
end Lire;
entry Lire_Sous_Conditions(V: out Valeur_Registre) when ( La_Valeur(1) < 0 ) is begin
V := La_Valeur ;
end Lire_Sous_Conditions;
procedure Decremente is
begin
for I in La_Valeur ‘range loop Barrière associée à l’entrée La_Valeur(I) := La_Valeur(I) – 1;
end loop;
end Incremente;
end Le_Registre ;
La barrière peut «!s’ouvrir!»
Synchrronisationistio en C // Posixix
Méécaniscanismeses étudiésétudiés
n Mutex Posix (sémaphore binaire)
n Sémaphores Posix
n Moniteurs Posix : variables conditionnelles associées aux Mutex
n Nous n’abordons pas
– l’utilisation de tube (« pipe »)
– l’utilisation des « sockets » (API TCP)
– l’utilisation des sémaphores à la Unix (tableau de sémaphores)
Gestion des Mutex
n Un « Mutex » est un sémaphore binaire pouvant prendre un des deux états « lock » (verrouillé) ou « unlock » (dévérrouillé)
n Un « Mutex » ne peut être partagé que par des thread d’un même process
n Un « Mutex » ne peut être verrouillé que par un seul thread à la fois.
n Un thread qui tente de verrouiller un « Mutex » déjà verrouillé est suspendu jusqu’à ce que le « Mutex » soit déverrouillé.
n Synchronisation en Ada
– Présentation des objets protégés d’Ada
– Sémantique de base
– Utilisation des entrées
n Synchronisation en C/Posix
– Les sémaphores Posix
– Les variables conditionnelles Posix
– Exemple d’utilisation