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