Cours 3: Synchronisation entre processus : suite de l`étude des

Transcription

Cours 3: Synchronisation entre processus : suite de l`étude des
Cours 3: Synchronisation entre
processus : suite de l’étude des
outils
Batterie d’algorithmes et outils de
synchronisation : 2 catégories
(Masquer les interruptions -> plus de concurrence -> plus de
pbms !)
Attente active : quelque chose qui représente la
condition, plus un algorithme qui met en place le test
et l’attente (semi-)active (« spinlock », « verrou tournant »)
Des variables partagées (« solution purement logicielle ») pour
exprimer la condition
Solutions impliquant le matériel :
instruction assembleur: Test-and-set
Instruction assembleur: swap
Attente passive (plus haut niveau d’abstraction) :
Sémaphore (IPC Unix semop, Java 1.5+ )
Moniteur avec variable(s) conditionnelle(s) (Concurrent
Pascal, Java)
Verrou conditionnel cad mutex+var. condition (POSIX pthread,
Java 1.5+ )
2
1
Outil de synchronisation Sémaphore
(Dyjsktra 1965)
Structure de données et des méthodes
Entier
File d’attente de processus/threads
Opérations pour
Initialiser l’entier : nombre de jetons
Prendre un “jeton”, ou s’endormir si pas de disponible : P(sem)
Réveiller un processus qui attend le jeton, en le lui passant,
sinon, le deposer : V(sem)
Outil général, comme moniteur, pour
le problème de l’exclusion mutuelle : 1 jeton initial
Exclusion mutuelle sur N exemplaires équivalents d’une même
ressource : N jetons
N’importe quelle forme de synchronisation conditionnelle : en
jouant sur la valeur du jeton et sur les appels P ou V
3
Détail des opérations sur sémaphore
: une variante possible
structure de données :
Val : entier
File : file de (contexte de)
processus ou threads
Opé
Opération Init(
Init(s:semaphore,
s:semaphore, int init)
init)
s.Val = init ;
creer s.File,
s.File, vide
Opé
Opération P(s:semaphore
P(s:semaphore))
Si (s.Val
(s.Val <=0)
Alors <ranger le contexte du processus dans s.File>
<mettre le processus dans l’é
tat bloqué
l’état
bloqué>
Sinon s.Val := s.Val – 1;
FinSi
Opé
Opération V(s:semaphore
V(s:semaphore))
Si (s.File
(s.File non vide)
Alors <retirer de s.File le contexte d’
d’un processus>
<mettre ce processus dans l’é
tat prêt>
l’état
Sinon s.Val := s.Val + 1;
FinSi
4
2
Exclusion mutuelle avec sémaphore
Simple !
P1
Semaphore sem_mutex commun à tous
…
Init(sem_mutex,
Init(sem_mutex, 1)
P2
…
…
P(sem_mutex
);
P(sem_mutex);
<section critique>
V(sem_mutex
);
V(sem_mutex);
P(sem_mutex
);
P(sem_mutex);
<section critique>
V(sem_mutex
);
V(sem_mutex);
…
…
5
Partage d’un pool de N exemplaires
équivalents d’une même ressource
Ex: un exemplaire = une case d’un tampon
N exemplaires = le tampon dispose de N cases
Semaphore sem_exemplaires commun à tous
…
Init(sem_exemplaires,
Init(sem_exemplaires, N)
P1
P2
…
…
P(sem_exemplaires
);
P(sem_exemplaires);
<section critique
utilisant un des N
exemplaires
disponible>
V(sem_exemplaires
);
V(sem_exemplaires);
…
P(sem_exemplaires
);
P(sem_exemplaires);
<section critique
utilisant un des N
exemplaires
disponible>
V(sem_exemplaires
);
V(sem_exemplaires);
…
6
3
Exemple de synchronisation
conditionnelle avec sémaphore
var. commune :
A (fichier, variable, …qui doit exister et contenir des donnees,
donnees, ou avoir une valeur)
P
A_est_remplie : sé
sémaphore INIT 0
…
…
Q
…
/* Jusque là
là, peu importe la valeur
de A */
Ecrire(A, valeur)
/* Maintenant, la valeur de A est
significative */
V(A_est_remplie
V(A_est_remplie))
/* Avant d’
d’utiliser A, il faut
qu’
qu’elle ait une valeur
significative */
P(A_est_remplie
P(A_est_remplie))
Lire(A)
…
…
7
Implanter l’outil sémaphore avec
l’outil moniteur (Java)
Bon exercice pour bien saisir les différences et les
subtilités d’implémentation
class Semap {
int Val;
public synchronized void Init(int v) {
Val=v;
}
public synchronized void P() {
if ou while 1 (Val = 0) {
wait();
}
2 Val = Val – 1;
}
public synchronized void V() {
Val = Val + 1;
notify(); // ou notifyAll();
}
}
Entrelacer des P ?
Test et modif. de Val:
dans une SC ? OUI
1 et si test False, 2
Entrelacer des P et V ?
Test de val et
manipulation de la
file: dans une SC ? OUI
1 8
4
Bilan des mises en oeuvre d’outils
de synchronisation
Pour implanter les outils de synchronisation avec
attente (forcément!), on a besoin d’autres outils pour
gérer de l’exclusion mutuelle
atomicité d’une séquence d’instructions, aussi (de)bloquer un
processus
en pratique, ce sont des solutions de plus bas niveau
d’abstraction. Exemples :
Masquer les interruptions durant l’exécution de P
ou utiliser l’algo avec TAS, basé sur attente active pour
implanter la primitive P
Bloquer un processus, ranger son contexte, le rendre éligible à
partir de son contexte, dans l’implantation d’un P ou V
Ou utiliser des sémaphores ou des verrous (sémaphore ultra
simplifié) pour implanter un moniteur
9
Principe mise en oeuvre moniteur en
Java
Tout objet et toute classe possèdent un verrou
transparent pour le programmeur (intrinsic lock)
Gestion FIFO / non FIFO dépend du JDK
C’est quoi un verrou ?
simplement un outil de synchro bloquant sur une condition
“libre” ou “occupé”
Qui peut s’implanter avec attente active ou passive
Méthodes synchronisées :
Méthode d’instance :
currentObject.lock()
<méthode>
currentObject.unlock()
Méthode de classe (static):
currentClass.lock()
<méthode>
currentClass.unlock()
10
5
Mise en oeuvre moniteur en Java,
suite
Tout objet / classe synchronisé(e) possède un
verrou et une file de processus bloqués
wait() {
put(current_thread, blocked)
current.unlock()
<stop the current thread>
current.lock() // on acquiert le verrou en se réveillant,
//car, l’exécution se poursuivra DANS le moniteur
}
notify() {
if !empty(blocked) {
thread = get(blocked)
wakeup(thread)
}
}
11
Producteurs / Consommateurs avec
sémaphore
P1
C1
P2
C2
Buffer (circulaire) avec nombre borné de places
2 Sémaphores pour la synchronisation conditionnelle :
Si plus de place disponible, processus type producteur attend
Si pas de place remplie, processus type consommateur attend
Sémaphore pour gérer exclusion mutuelle entre
producteurs ou entre consommateurs pour ne pas
qu’ils agissent sur la même case
Variante: un sémaphore pour le coté producteur, et un
sémaphore pour le coté consommateur
On peut démontrer que les producteurs ne peuvent jamais
être en train de remplir une case, et en même temps, les
consommateurs en train de vider cette même case
12
6
Algorithme Prod/Cons avec
sémaphore
int bufferSz;
Msg[] buffer = new buffer[bufferSz];
Semaphore mutexIn = new Semaphore(1);
Semaphore mutexOut = new Semaphore(1);
Semaphore notFull = new Semaphore(bufferSz);
Semaphore notEmpty = new Semaphore(0);
Produce(Msg msg) {
// if buffer is full, wait for one empty entry
notFull.P();
mutexIn.P();
buffer[in] = msg;
in = in + 1 % bufferSize;
mutexIn.V();
// wakeup some waiting process
notEmpty.V();
}
Msg Consume() {
// if buffer is empty, wait for one item
notEmpty.P();
mutexOut.P();
Msg msg = buffer[out];
out = out + 1 % bufferSz;
mutexOut.V();
notFull.V();
return msg;
}
13
Classe Sémaphore de Java 1.5+
class Semaphore { // import java.util.concurrent.Semaphore
public void acquire()
// Acquires a permit from this semaphore, blocking until one is available, or the
// thread is interrupted.
public void acquire(int permits)
// Acquires the given number of permits …
public int availablePermits()
// Returns the current number of permits available in this semaphore.
public Collection<Thread> getQueuedThreads()
// Returns a collection containing threads that may be waiting to acquire.
public boolean isFair()
// Returns true if this semaphore has fairness set true.protected => FIFO order in the
queue
public void release()
// Releases a permit, returning it to the semaphore.
public boolean tryAcquire()
// Acquires a permit from this semaphore, only if one is available at the time of
invocation.
…
}
14
7
“Blocking queue” en Java
Modèle producteur consommateur pour insérer et
retirer des éléments d’une collection, en se
bloquant si nécessaire
BlockingQueue q = new SomeQueueImplementation(); //Array, LinkedList…
class Producer implements Runnable {
private final BlockingQueue queue;
Producer(BlockingQueue q) { queue = q; }
public void run() {
class Consumer implements Runnable {
private final BlockingQueue queue;
Consumer(BlockingQueue q) { queue = q; }
public void run() {
try { while(true)
try { while(true)
queue.put(produce()); }
consume(queue.take()); }
catch (InterruptedException ex) {}
catch (InterruptedException ex) {}
}
}
Object produce() { ... }
void consume(Object x) { ... }
}
}
15
Bilan: quel outil utiliser… tout dépend
Pour un même problème qui correspond par exemple
au producteur / consommateur, on peut utiliser
Moniteur plus variable de condition implicite ou plusieurs
variables de condition explicites (voir prochain cours)
Sémaphores uniquement
Combinaison des deux
Structure de données qui réalise les synchronisations
nécéssaires
Ou, tests avec attente active
Tout dépendra du niveau d’abstraction où on se place
Programmation au coeur même du noyau du SE
Implantation fonction système de synchro (semop, read ou
write sur un tube, ou msgsnd, msgrcv)
Implantation d’une primitive de bibliothèque de threads
utilisateur ou de machine virtuelle
Application multi-thread cherchant à tout prix à masquer les
problèmes de synchronisation
16
8