Vous êtes sur la page 1sur 102

Objectifs de la matière

Objectif
Être capable de comprendre et développer des applications
parallèles (concurrentes)
Systèmes concurrents
→ modélisation pour la conception de programmes parallèles
Philippe Quéinnec → connaissance des schémas (patrons) essentiels
→ raisonnement sur les programmes parallèles : exécution,
Département Informatique et Mathématiques appliquées propriétés
ENSEEIHT
→ pratique de la programmation parallèle avec un environnement
proposant les objets/outils de base
8 novembre 2017
pour la programmation d’activités concurrentes (gros grain) :
Java, principalement
pour la programmation parallèle (grain fin) : OpenMP

Systèmes concurrents 1 / 37

Organisation de la matière
Cours (40%) : définitions, principes, modèles Plan du cours
TD (20%) : conception et méthodologie
TP (30%) : implémentation des schémas et principes
1 Introduction : problématique
Evaluation
2 Exclusion mutuelle
BE OpenMP
3 Synchronisation à base de sémaphores
Projet (10%) application de synthèse : réalisation d’un service
de support à la programmation concurrente, parallèle ou 4 Interblocage
répartie. 5 Synchronisation à base de moniteur
Travail en groupe de 3 ± 1 (selon le thème du projet) 6 API Java, Posix Threads
présentation mi-octobre
suivi + points d’étape réguliers 7 Processus communicants – Ada
rendu final début janvier 8 Transactions – mémoire transactionnelle
Examen : écrit, sur la conception de systèmes concurrents. 9 Synchronisation non bloquante
Page de l’enseignement : 10 Calcul parallèle : OpenMP, etc [A.Buttari]
http://queinnec.perso.enseeiht.fr/Ens/sc.html
http://moodle-n7.inp-toulouse.fr
Contact : mauran@enseeiht.fr, queinnec@enseeiht.fr Systèmes concurrents 4 / 37
Activités concurrentes Activités concurrentes
Conception Conception
Avantages/inconvénients Avantages/inconvénients

Contenu de cette partie

Première partie Nature et particularités des programmes concurrents


⇒ conception et raisonnement systématiques et rigoureux
Modélisation des systèmes concurrents
Introduction Points clés pour faciliter la conception des applications
concurrentes
Intérêt et limites de la programmation parallèle
Mise en œuvre de la programmation concurrente sur les
architectures existantes

Systèmes concurrents 5 / 37 Systèmes concurrents – Introduction 6 / 37

Activités concurrentes Le problème Activités concurrentes Le problème


Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Plan Le problème

1 Activités concurrentes Ouvrier


Récepteur travaux urgents
Le problème
Un peu d’architecture Ouvrier Résultats
Livreur
Récepteur (émetteur)
Communication & activités
Ouvrier

2 Conception Récepteur travaux normaux


Ouvrier
Comment contrôler (réaliser) la composition ?
Comment décrire ?
Comment raisonner ? coopération : les activités  se connaissent 

compétition : les activités  s’ignorent 


3 Avantages/inconvénients vitesse d’exécution arbitraire

Systèmes concurrents – Introduction 7 / 37 Systèmes concurrents – Introduction 8 / 37


Activités concurrentes Le problème Activités concurrentes Le problème
Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Intérêt de la programmation concurrente Nécessité de la programmation concurrente

La puissance de calcul monoprocesseur atteint un plafond


Facilité de conception
le parallélisme est naturel sur beaucoup de systèmes l’augmentation des performances d’un processeur dépend
directement de sa fréquence d’horloge f
temps réel : systèmes embarqués, applications multimédia
l’énergie consommée et dissipée augmente comme f 3
mode de fonctionnement : modélisation et simulation de
→ une limite physique est atteinte depuis quelques années
systèmes physiques, d’organisations, systèmes d’exploitation
Les gains de parallélisme au niveau mono-processeur sont
Pour accroı̂tre la puissance de calcul
limités : processeurs vectoriels, architectures pipeline, GPU
algorithmique parallèle et répartie
conviennent mal à des calculs irréguliers/généraux
Pour faire des économies
La loi de Moore reste valide : la densité des transistors double
mutualisation de ressources coûteuses via un réseau
tous les 18 à 24 mois
Parce que la technologie est mûre
banalisation des systèmes multi-processeurs, des stations de Les architectures multiprocesseurs sont (pour l’instant) le principal
travail/ordinateurs en réseau, services répartis moyen d’accroı̂tre la puissance de calcul

Systèmes concurrents – Introduction 9 / 37 Systèmes concurrents – Introduction 10 / 37

Activités concurrentes Le problème Activités concurrentes Le problème


Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Concurrence vs parallélisme Différence avec la programmation séquentielle


Activités ± simultanées ⇒ explosion de l’espace d’états
Parallélisme
P1 P2
Exécution simultanée de plusieurs codes. for i := 1 to 10 for j := 1 to 10
print(i) print(j)
Concurrence
Structuration d’un programme en activités ± indépendantes qui P1 seul → 10 états ,
P1 k P2 → 10 x 10 = 100 états /
interagissent et se coordonnent.
P1 ; P2 → 1 exécution ,
Pas de concurrence Concurrence P1 k P2 → 184756 exécutions /
Pas de parallélisme prog. séquentiel multi-activités Interdépendance des activités
sur un mono-processeur logique : production/utilisation de résultats intermédiaires
chronologique : disponibilité des résultats
Parallélisme parallélisation multi-activités
⇒non déterminisme
automatique / sur un multi-processeurs
implicite ⇒ nécessité de méthodes et d’outils (conceptuels et logiciels) pour
le raisonnement et le développement
Systèmes concurrents – Introduction 11 / 37 Systèmes concurrents – Introduction 12 / 37
Activités concurrentes Le problème Activités concurrentes Le problème
Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Composants matériels Processeur

Vision simpliste : à chaque cycle, le processeur exécute une


instruction machine à partir d’un flot séquentiel (le code).
Architecture d’un ordinateur : En pratique :
Processeurs pipeline : plusieurs instructions en cours dans un même cycle :
Mécanisme d’interconnexion obtention, décodage, exécution, écriture du résultat
Mémoire et caches superscalaire : plusieurs unités d’exécution
instructions vectorielles
réordonnancement (out-of-order)

Systèmes concurrents – Introduction 13 / 37 Systèmes concurrents – Introduction 14 / 37

Activités concurrentes Le problème Activités concurrentes Le problème


Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Interconnexion Mémoire
La mémoire et le processeur sont éloignés : un accès mémoire est
considérablement plus lent que l’exécution d’une instruction (peut
atteindre un facteur 100 dans un ordinateur, 10000 en réparti).
Bus unique Principe de localité :
interconnecte des processeurs entre eux
interconnecte les processeurs et la mémoire temporelle si on utilise une adresse, on l’utilisera probablement
interconnecte les processeurs et des unités d’E/S de nouveau dans peu de temps
médium à diffusion : usage exclusif spatiale si on utilise une adresse, on utilisera probablement
Plusieurs bus dédiés une adresse proche dans peu de temps
Mini réseaux locaux (parallélisme massif) ⇒ conserver près du CPU les dernières cases mémoire accédées
Réseaux locaux classiques (système réparti) ⇒ Cache : mémoire rapide proche du processeur

Plusieurs niveaux de caches : de plus en plus gros, de moins en


moins rapides (actuellement 3 niveaux).
CPU Cache L1 Cache L2 Mémoire

Systèmes concurrents – Introduction 15 / 37 Systèmes concurrents – Introduction 16 / 37


Activités concurrentes Le problème Activités concurrentes Le problème
Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Cache Architecture multi-processeurs


Multi-processeurs  à l’ancienne  :
Bus
CPU Cache L1 Cache L2

CPU Cache L1 Cache L2


Différents principes de placement dans le cache (associatif, Mémoire
CPU Cache L1 Cache L2
mappé, k-associatif. . . )
Différentes stratégies de remplacement (LRU - least recently CPU Cache L1 Cache L2
used. . . )
Multi-processeurs multi-cœurs :
Différentes stratégies d’invalidation - cohérence mémoire
CPU Cache L1
Cache L2
CPU Cache L1
Mémoire
CPU Cache L1
Cache L2
CPU Cache L1

Systèmes concurrents – Introduction 17 / 37 Systèmes concurrents – Introduction 18 / 37

Activités concurrentes Le problème Activités concurrentes Le problème


Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Architecture multi-processeurs Écritures en mémoire

SMP Symmetric multiprocessor : une mémoire + un Comme fonctionne l’écriture d’une case mémoire en présence de
ensemble de processeurs caches ?
CPU Write-Through diffusion sur le bus à chaque valeur écrite
CPU RAM + visible par les autres processeurs ⇒ invalidation
CPU des valeurs passées
NUMA Non-Uniform Memory Access : un graphe + la mémoire et le cache sont cohérents
d’interconnexion de {CPU+mémoire} − trafic inutile : écritures répétées, écritures de
CPU CPU CPU variables privées au thread
RAM RAM RAM
Write-Back diffusion uniquement à l’éviction de la ligne
CPU CPU CPU
RAM RAM RAM + trafic minimal
CC-NUMA Cache-Coherent Non-Uniform Memory Access − cohérence cache - mémoire - autres caches ?

Systèmes concurrents – Introduction 19 / 37 Systèmes concurrents – Introduction 20 / 37


Activités concurrentes Le problème Activités concurrentes Le problème
Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Cohérence mémoire Cohérence Mémoire – exemple


Si un processeur écrit la case d’adresse a1 , quand les autres
processeurs verront-ils cette valeur ? Si plusieurs écritures
consécutives en a1 , a2 . . . , sont-elles vues dans cet ordre ? Et les
lectures indépendantes d’une écriture ? Init : x = 0 ∧ y = 0
Règles de cohérence mémoire
Processeur P1 Processeur P2
Cohérence séquentielle le résultat d’une exécution parallèle est le
même que celui d’une exécution séquentielle qui (1) x ← 1 (a) y ← 1
respecte l’ordre partiel de chacun des processeurs. (2) t1 ← y (b) t2 ← x
Cohérence PRAM (pipelined RAM ou fifo) les écritures d’un
même processeur sont vues dans l’ordre où elles ont Un résultat t1 = 0 ∧ t2 = 0 est possible en cohérence PRAM et
été effectuées ; des écritures de processeurs différents slow, impossible en cohérence séquentielle.
peuvent être vues dans des ordres différents.
Cohérence  lente  (slow consistency) : une lecture retourne une
valeur précédemment écrite, sans remonter dans le
temps.
Systèmes concurrents – Introduction 21 / 37 Systèmes concurrents – Introduction 22 / 37

Activités concurrentes Le problème Activités concurrentes Le problème


Conception Un peu d’architecture Conception Un peu d’architecture
Avantages/inconvénients Communication & activités Avantages/inconvénients Communication & activités

Activité Interaction par mémoire partagée

Système centralisé multi-tâches


communication implicite, résultant de l’accès par chaque
activité à des variables partagées
Activité/processus/tâches/threads/processus légers/. . . activités anonymes (interaction sans identification)
exécution d’un programme séquentiel coordination (synchronisation) nécessaire (pour déterminer
l’instant où une interaction est possible)
entité logicielle
exécutable par un processeur Activité

Mémoire
interruptible et commutable Activité

Activité
Exemples
multiprocesseurs à mémoire partagée,
processus légers,
Unix : couplage mémoire (mmap), fichiers
Systèmes concurrents – Introduction 23 / 37 Systèmes concurrents – Introduction 24 / 37
Activités concurrentes Le problème Activités concurrentes Comment contrôler (réaliser) la composition ?
Conception Un peu d’architecture Conception Comment décrire ?
Avantages/inconvénients Communication & activités Avantages/inconvénients Comment raisonner ?

Interaction par échange de messages Plan


Activités communiquant par messages
communication explicite par transfert de données (messages) 1 Activités concurrentes
désignation nécessaire du destinataire (ou d’un canal de Le problème
communication) Un peu d’architecture
coordination implicite, découlant de la communication Communication & activités

Mémoire Activité
2 Conception
Comment contrôler (réaliser) la composition ?
Medium Activité Mémoire
Comment décrire ?
Mémoire Activité Comment raisonner ?

Exemples 3 Avantages/inconvénients
processeurs en réseau,
architectures logicielles réparties (client/serveur. . . ),
Unix : tubes, signaux
Systèmes concurrents – Introduction 25 / 37 Systèmes concurrents – Introduction 26 / 37

Activités concurrentes Comment contrôler (réaliser) la composition ? Activités concurrentes Comment contrôler (réaliser) la composition ?
Conception Comment décrire ? Conception Comment décrire ?
Avantages/inconvénients Comment raisonner ? Avantages/inconvénients Comment raisonner ?

Contrôler Contrôler

Concevoir une application concurrente


Hypothèses de bon comportement des activités : un protocole
contrôler un ensemble d’activités concurrentes
définit les interactions possibles :
contrôler la progression et les interactions de chaque activité
Opérations et paramètres autorisés.
assurer leur protection réciproque
Séquences d’actions autorisées.
Moyen Un ouvrier ne doit pas déposer plus de résultats qu’il n’a pris
de travaux.
attente (par blocage – suspension – de l’activité)

Systèmes concurrents – Introduction 27 / 37 Systèmes concurrents – Introduction 28 / 37


Activités concurrentes Comment contrôler (réaliser) la composition ? Activités concurrentes Comment contrôler (réaliser) la composition ?
Conception Comment décrire ? Conception Comment décrire ?
Avantages/inconvénients Comment raisonner ? Avantages/inconvénients Comment raisonner ?

Décrire Décrire

Triplets de Hoare
précondition/action/postcondition
Compteurs d’événements
Compter les actions ou les changements d’états et les relier entre
Exemple
eux.
{t = nb travaux en attente ∧ t > 0 ∧ r = nb résultats}
Exemple ouvrier effectue un travail
{nb travaux en attente = t − 1 ∧ nb résultats = r + 1}
#nb de travaux soumis = #nb travaux effectués
+ #nb travaux en cours Sérialisation : {p}A1 ; A2 {q12 }, {p}A2 ; A1 {q21 }
+ #nb travaux en attente {p}A1 k A2 {q12 ∨ q21 }
Indépendance :
A1 et A2 sans interférence, {p}A1 {q1 }, {p}A2 {q2 }
{p}A1 k A2 {q1 ∧ q2 }

Systèmes concurrents – Introduction 29 / 37 Systèmes concurrents – Introduction 30 / 37

Activités concurrentes Comment contrôler (réaliser) la composition ? Activités concurrentes Comment contrôler (réaliser) la composition ?
Conception Comment décrire ? Conception Comment décrire ?
Avantages/inconvénients Comment raisonner ? Avantages/inconvénients Comment raisonner ?

Décrire Modèle : l’entrelacement


Propriétés temporelles Raisonner sur tous les cas parallèles est trop complexe
⇒ on raisonne sur des exécutions séquentielles obtenues par
Linéaires : pour toutes les exécutions possibles, entrelacement des instructions des différentes activités.
à tout moment d’une exécution.
Arborescentes : pour certaines exécutions possibles, Deux activités P = p1 ; p2 et Q = q1 ; q2 . L’exécution concurrente
à tout moment d’une exécution. PkQ est vue comme (équivalente à) l’une des exécutions :
p1 ; p2 ; q1 ; q2 ou p1 ; q1 ; p2 ; q2 ou p1 ; q1 ; q2 ; p2 ou q1 ; p1 ; p2 ; q2 ou
q1 ; p1 ; q2 ; p2 ou q1 ; q2 ; p1 ; p2
Exemple
(p+q)!
Sûreté : rien de mauvais ne se produit Nombre d’entrelacements : p! q!
Deux ouvriers ne peuvent jamais prendre le même travail.
Attention
Vivacité : quelque chose de bon finit par se produire
Ne pas oublier que c’est un modèle simplificateur (vraie
Un travail déposé finit par être pris par un ouvrier.
concurrence, cohérence mémoire. . . )
Possibilité : deux travaux déposés consécutivement peuvent
Il peut ne pas exister de code séquentiel équivalent au code
être exécutés séquentiellement par le même ouvrier.
parallèle.
Systèmes concurrents – Introduction 31 / 37 Systèmes concurrents – Introduction 32 / 37
Activités concurrentes Comment contrôler (réaliser) la composition ? Activités concurrentes
Conception Comment décrire ? Conception
Avantages/inconvénients Comment raisonner ? Avantages/inconvénients

Raisonner Plan

1 Activités concurrentes
Le problème
Un peu d’architecture
Contrôler les effets des interactions
Communication & activités
isoler (raisonner indépendamment) ⇒ modularité
contrôler/spécifier l’interaction 2 Conception
schémas connus d’interaction (design patterns) Comment contrôler (réaliser) la composition ?
Comment décrire ?
Comment raisonner ?

3 Avantages/inconvénients

Systèmes concurrents – Introduction 33 / 37 Systèmes concurrents – Introduction 34 / 37

Activités concurrentes Activités concurrentes


Conception Conception
Avantages/inconvénients Avantages/inconvénients

Avantages/inconvénients Parallélisme et performance


Mythe du parallélisme
+ utilisation d’un système multi-processeurs.
 Si je remplace ma machine mono-processeur par une machine à
+ utilisation de la concurrence naturelle d’un programme. N processeurs, mon programme ira N fois plus vite 
+ modèle de programmation naturel, en explicitant la
synchronisation nécessaire. Soit un système composé par une partie p parallélisable + une
partie 1 − p séquentielle.
− surcoût d’exécution (synchronisation, implantation du Loi d'Amdahl
20.00
pseudo-parallélisme). 18.00
Portion parallèle

− surcoût de développement : nécessité d’expliciter la 16.00 50%


75%

CPU durée p = 40% p = 80% 14.00 90%

synchronisation, vérifier la réentrance des bibliothèques, 12.00


95%

1 p + (1 − p) 100 100

Speedup
10.00
danger des variables partagées. 4 p
+ (1 − p) 70 40 8.00
4
p
− surcoût de mise-au-point : debuggage souvent délicat (pas de 8 8 + (1 − p) 65 30 6.00

4.00
p
flot séquentiel à suivre) ; effet d’interférence entre des 16 16 + (1 − p) 62, 5 25 2.00

activités, interblocage. . . ∞ 60 20 0.00

16384

32768

65536
1024

2048

4096

8192
128

256

512
16

32

64
1

8
1
Loi d’Amdahl : maximal speedup = 1−p
Nombre de Processeurs

(source : wikicommons)

Systèmes concurrents – Introduction 35 / 37 Systèmes concurrents – Introduction 36 / 37


Activités concurrentes
Conception
Avantages/inconvénients

Parallélisme et performance
Mythe de la performance
 Si je remplace ma machine par une machine N fois plus rapide,

mon programme traitera des problèmes N fois plus grands dans le


même temps 

Pour un problème de taille n soluble en temps T , taille de problème


soluble dans le même temps sur une machine N fois plus rapide :
complexité N=4 N = 16 N = 1024
O(n) √ 4n √ 16n √ 1024n
O(n2 ) √ 4n = 2n √ 16n = 4n √ 1024n = 32n
3
O(n ) 3
4n ≈ 1.6n 3
16n ≈ 2.5n 3
1024n ≈ 10n
O(e n ) ln(4)n ≈ 1.4n ln(16)n ≈ 2.8n ln(1024)n ≈ 6.9n
En supposant en outre que tout est 100% est parallélisable et qu’il
n’y a aucune interférence !
Systèmes concurrents – Introduction 37 / 37
Interférences entre actions Interférences entre actions
Mise en œuvre Mise en œuvre

Contenu de cette partie

Deuxième partie
Difficultés résultant d’accès concurrents à un objet partagé
Mise en œuvre de protocoles d’isolation
solutions synchrones (i.e. bloquantes) : attente active
L’exclusion mutuelle → difficulté du raisonnement en algorithmique concurrente
→ aides fournies au niveau matériel
solutions asynchrones : gestion des processus

Systèmes concurrents 2 / 26 Systèmes concurrents – Exclusion mutuelle 3 / 26

Interférences entre actions Isolation Interférences entre actions Isolation


Mise en œuvre L’exclusion mutuelle Mise en œuvre L’exclusion mutuelle

Plan Interférence et isolation

(1) x := lire compte(2);


(a) v := lire compte(1);
(2) y := lire compte(1);
1 Interférences entre actions (b) v := v - 100;
(3) y := y + x;
Isolation (4) ecrire compte(1, y);
(c) ecrire compte(1, v);
L’exclusion mutuelle
Le compte 1 est partagé par les deux traitements ;
les variables x, y et v sont locales à chacun des traitements ;
2 Mise en œuvre
Solutions logicielles les traitements s’exécutent en parallèle, et leurs actions
Solutions matérielles peuvent être entrelacées.
Primitives du système d’exploitation (1) (2) (a) (3) (b) (4) (c) est une exécution possible, incohérente.
En pratique. . . (1) (2) (3) (4) (a) (b) (c) est une exécution possible, cohérente.
(1) (a) (b) (c) (2) (3) (4) " " " " "

Systèmes concurrents – Exclusion mutuelle 4 / 26 Systèmes concurrents – Exclusion mutuelle 5 / 26


Interférences entre actions Isolation Interférences entre actions Isolation
Mise en œuvre L’exclusion mutuelle Mise en œuvre L’exclusion mutuelle

Section critique Accès concurrents

Modification concurrente
Définition h x := 0x 00 01 i k h x := 0x 02 00 i
Les séquences S1 = (1);(2);(3);(4) et S2 = (a);(b);(c) sont ⇒ x = 0x0001 ou 0x0200 ou 0x0201 ou 0xOO00 ou 1234 !
des sections critiques, qui doivent chacune être exécutées de
manière atomique (indivisible) : Exécution concurrente
le résultat de l’exécution concurrente de S1 et S2 doit être le init x = 0;
même que celui de l’une des exécutions séquentielles S1 ; S2 ou h a := x; x := a + 1 i k h b := x; x := b - 1 i
S2 ; S1 . ⇒ x = -1, 0 ou 1
cette équivalence peut être atteinte en contrôlant directement
Cohérence mémoire
l’ordre d’exécution de S1 et S2 (exclusion mutuelle), ou en
contrôlant les effets de S1 et S2 (contrôle de concurrence). init x = 0 ∧ y = 0
h x := 1; y := 2 i k h printf("%d %d",y,x); i
⇒ affiche 0 0 ou 2 1 ou 0 1 ou 2 0 !

Systèmes concurrents – Exclusion mutuelle 6 / 26 Systèmes concurrents – Exclusion mutuelle 7 / 26

Interférences entre actions Isolation Interférences entre actions Isolation


Mise en œuvre L’exclusion mutuelle Mise en œuvre L’exclusion mutuelle

L’exclusion mutuelle Propriétés

Exécution en exclusion mutuelle d’un ensemble de sections critiques (sûreté) à tout moment, au plus une activité est en cours
d’exécution d’une section critique
ensemble d’activités concurrentes Ai
variables partagées par toutes les activités invariant ∀i, j ∈ 0..N − 1 : Ai .excl ∧ Aj .excl ⇒ i = j
variables privées (locales) à chaque activité
structure des activités (progression) lorsqu’il y a (au moins) une demande,
une activité qui demande à entrer sera admise
cycle
entrée section critique sortie (∃i ∈ 0..N − 1 : Ai .dem) (∃j ∈ 0..N − 1 : Aj .excl)
.
.
. ∀i ∈ 0..N − 1 : Ai .dem (∃j ∈ 0..N − 1 : Aj .excl)
fincycle
hypothèses : (vivacité individuelle) si une activité demande à entrer, elle
finira par obtenir l’accès (son attente est finie)
vitesse d’exécution non nulle
section critique de durée finie
∀i ∈ 0..N − 1 : Ai .dem Ai .excl

Systèmes concurrents – Exclusion mutuelle 8 / 26 Systèmes concurrents – Exclusion mutuelle 9 / 26


Solutions logicielles Solutions logicielles
Interférences entre actions Solutions matérielles Interférences entre actions Solutions matérielles
Mise en œuvre Primitives du système d’exploitation Mise en œuvre Primitives du système d’exploitation
En pratique. . . En pratique. . .

Plan Comment ?

1 Interférences entre actions


Isolation
L’exclusion mutuelle attente active : tester en permanence la possibilité d’entrer
mécanismes matériels
simplifiant l’attente active (instructions spécialisées)
2 Mise en œuvre évitant l’attente active (masquage des interruptions)
Solutions logicielles
Solutions matérielles primitives du système d’exploitation/d’exécution
Primitives du système d’exploitation
En pratique. . .

Systèmes concurrents – Exclusion mutuelle 10 / 26 Systèmes concurrents – Exclusion mutuelle 11 / 26

Solutions logicielles Solutions logicielles


Interférences entre actions Solutions matérielles Interférences entre actions Solutions matérielles
Mise en œuvre Primitives du système d’exploitation Mise en œuvre Primitives du système d’exploitation
En pratique. . . En pratique. . .

Une fausse solution Alternance

Algorithme
Algorithme tour : global 0..1;
occupé : global boolean := false; tant que tour 6= i faire nop;
section critique
tant que occupé faire nop;
tour ← i + 1 mod 2;
occupé ← true;
section critique
occupé ← false; note : i = identifiant de l’activité demandeuse
deux activités (généralisable à plus)
(Test-and-set non atomique) lectures et écritures atomiques
alternance obligatoire

Systèmes concurrents – Exclusion mutuelle 12 / 26 Systèmes concurrents – Exclusion mutuelle 13 / 26


Solutions logicielles Solutions logicielles
Interférences entre actions Solutions matérielles Interférences entre actions Solutions matérielles
Mise en œuvre Primitives du système d’exploitation Mise en œuvre Primitives du système d’exploitation
En pratique. . . En pratique. . .

Priorité à l’autre demandeur Peterson 1981

Algorithme Algorithme
demande : global array 0..1 of boolean; demande : global array 0..1 of boolean := [false, false];
demande[i] ← true; tour : global 0..1;
tant que demande[j] faire nop; demande[i] ← true;
section critique tour ← j;
demande[i] ← false; tant que (demande[j] et tour = j) faire nop;
section critique
demande[i] ← false;
i = identifiant de l’activité demandeuse
j = identifiant de l’autre activité
deux activités (non facilement généralisable)
deux activités (non facilement généralisable)
lectures et écritures atomiques
lectures et écritures atomiques
évaluation non atomique du  et 
risque d’attente infinie (interblocage)
vivacité individuelle
Systèmes concurrents – Exclusion mutuelle 14 / 26 Systèmes concurrents – Exclusion mutuelle 15 / 26

Solutions logicielles Solutions logicielles


Interférences entre actions Solutions matérielles Interférences entre actions Solutions matérielles
Mise en œuvre Primitives du système d’exploitation Mise en œuvre Primitives du système d’exploitation
En pratique. . . En pratique. . .

Solution pour n activités (Lamport 1974) Instruction matérielle

L’algorithme de la boulangerie
boolean choix[N]; Soit TestAndSet(x), instruction indivisible qui positionne x à vrai
int num[N]; et renvoie l’ancienne valeur :
choix[i] ← true;
int tour ← 0; Définition
pour k de 0 à N faire tour ← max(tour,num[k]); function TestAndSet (x : in out boolean) : boolean
num[i] ← tour + 1; declare oldx : boolean
choix[i] ← false; begin
oldx := x; x := true;
pour k de 0 à N faire
tant que (choix[k]) faire nop; return oldx;
tant que (num[k]6=0) ∧ (num[k],k)≺(num[i],i) faire nop; end TestAndSet
section critique
num[i] ← 0;

Systèmes concurrents – Exclusion mutuelle 16 / 26 Systèmes concurrents – Exclusion mutuelle 17 / 26


Solutions logicielles Solutions logicielles
Interférences entre actions Solutions matérielles Interférences entre actions Solutions matérielles
Mise en œuvre Primitives du système d’exploitation Mise en œuvre Primitives du système d’exploitation
En pratique. . . En pratique. . .

Utilisation du TestAndSet Utilisation de FetchAndAdd


Définition
Alors : protocole d’exclusion mutuelle : function FetchAndAdd (x : in out int) : int
declare oldx : int
Algorithme begin
occupé : global boolean := false; oldx := x; x := oldx + 1;
tant que TestAndSet(occupé) faire nop; return oldx;
end FetchAndAdd
section critique
occupé ← false; ticket : global int := 0;
tour : global int := 0;
montour : local int;
Tous les processeurs actuels possèdent une instruction analogue au
TestAndSet, et adaptée aux multi-processeurs symétriques. montour ← FetchAndAdd(ticket);
tant que tour 6= montour faire nop;
section critique
FetchAndAdd(tour);
Systèmes concurrents – Exclusion mutuelle 18 / 26 Systèmes concurrents – Exclusion mutuelle 19 / 26

Solutions logicielles Solutions logicielles


Interférences entre actions Solutions matérielles Interférences entre actions Solutions matérielles
Mise en œuvre Primitives du système d’exploitation Mise en œuvre Primitives du système d’exploitation
En pratique. . . En pratique. . .

Masquage des interruptions Le système d’exploitation

Éviter la préemption du processeur par une autre activité :


Algorithme
masquer les interruptions
section critique
1 contrôle de la préemption
démasquer les interruptions 2 contrôle de l’exécution des activités
3  effet de bord  d’autres primitives
plus d’attente !
mono-processeur seulement
pas d’entrée-sortie, pas de défaut de page en SC
→ µ-système embarqué

Systèmes concurrents – Exclusion mutuelle 20 / 26 Systèmes concurrents – Exclusion mutuelle 21 / 26


Solutions logicielles
Interférences entre actions Solutions matérielles
Primitives du système d’exploitation
Éviter l’attente active : contrôle des activités
Mise en œuvre
En pratique. . .
Algorithme
Ordonnanceur avec priorités occupé : global bool := false;
demandeurs : global fifo;
Ordonnanceur (scheduler) d’activités avec priorité fixe : l’activité bloc atomique
si occupé alors
de plus forte priorité s’exécute, sans préemption possible. self ← identifiant de l´ activité courante
ajouter self dans demandeurs
Algorithme se suspendre
priorité ← priorité max // pas de préemption possible sinon
occupé ← true
section critique
finsi
priorité ← priorité habituelle // avec préemption fin bloc
section critique
mono-processeur seulement bloc atomique
si demandeurs est non vide alors
les activités non concernées sont aussi pénalisées p ← extraire premier de demandeurs
débloquer p
entrée-sortie ? mémoire virtuelle ? sinon
→ système embarqué occupé ← false
finsi
fin bloc
Systèmes concurrents – Exclusion mutuelle 22 / 26

Solutions logicielles Solutions logicielles


Interférences entre actions Solutions matérielles Interférences entre actions Solutions matérielles
Mise en œuvre Primitives du système d’exploitation Mise en œuvre Primitives du système d’exploitation
En pratique. . . En pratique. . .

Le système de fichiers ( !) Le système de fichiers (2)

Opération atomique analogue au TestAndSet, basée sur l’existence Verrous coopératifs sur les fichiers : en lecture partagée, en écriture
et la création d’un fichier. exclusive (appels système : lockf, flock, fcntl)
Algorithme Algorithme
tant que
fd = open ("toto", O RDWR);
open("toto", O RDONLY | O EXCL | O CREAT, 0) == -1
faire nop; lockf (fd, F LOCK, 0); // verrouillage exclusif
section critique section critique
unlink("toto"); lockf (fd, F ULOCK, 0); // déverrouillage

ne nécessite pas de mémoire partagée attente passive (l’activité est bloquée)


atomicité assurée par le noyau d’exécution portabilité aléatoire

Systèmes concurrents – Exclusion mutuelle 24 / 26 Systèmes concurrents – Exclusion mutuelle 25 / 26


Solutions logicielles
Interférences entre actions Solutions matérielles
Mise en œuvre Primitives du système d’exploitation
En pratique. . .

La réalité
Actuellement, tout environnement d’exécution fournit un
mécanisme analogue aux verrous (locks), avec les opérations
atomiques :
obtenir (acquire) : si le verrou est libre, l’attribuer à l’activité
demandeuse ; sinon bloquer l’activité demandeuse
rendre/libérer (release) : si au moins une activité est en
attente du verrou, transférer la possession à l’un des
demandeurs et le débloquer ; sinon marquer le verrou comme
libre.
Algorithme
accès : global verrou // partagé
obtenir accès
section critique
libérer accès
Systèmes concurrents – Exclusion mutuelle 26 / 26
Spécification Spécification
Applications Applications
Implantation Implantation
Conclusion Conclusion

Contenu de cette partie

Troisième partie Présentation d’un objet de synchronisation élémentaire


(sémaphore)
Patrons de conception élémentaires utilisant les sémaphores
Sémaphore Exemple récapitulatif (schéma producteurs/consommateurs)
Schémas d’utilisation pour le contrôle fin de l’accès aux
ressources partagées
Mise en œuvre des sémaphores

Systèmes concurrents 2 / 33 Systèmes concurrents – Sémaphore 4 / 33

Spécification Définition Spécification Définition


Applications Spécification intuitive Applications Spécification intuitive
Implantation Spécification formelle : Hoare Implantation Spécification formelle : Hoare
Conclusion Ordonnancement Conclusion Ordonnancement

Plan But
1 Spécification
Définition
Spécification intuitive
Spécification formelle : Hoare Fournir un moyen simple, élémentaire, de contrôler les effets
Ordonnancement des interactions entre activités
isoler (modularité) : atomicité
2 Applications spécifier des interactions précises : synchronisation
Méthodologie
Exprimer ce contrôle par des interactions sur un objet partagé
L’exclusion mutuelle
(indépendant des activités en concurrence) plutôt que par des
L’allocateur de ressources critiques
interactions entre activités (dont le code et le comportement
Producteurs/consommateurs
seraient alors interdépendants)
3 Implantation
Sémaphore général à partir de verrous
Système d’exploitation
L’inversion de priorité
Systèmes concurrents – Sémaphore 5 / 33 Systèmes concurrents – Sémaphore 6 / 33
Spécification Définition Spécification Définition
Applications Spécification intuitive Applications Spécification intuitive
Implantation Spécification formelle : Hoare Implantation Spécification formelle : Hoare
Conclusion Ordonnancement Conclusion Ordonnancement

Définition – Dijkstra 1968 Spécification intuitive


Principes directeurs :
Définition
Abstraction de la file d’attente des activités bloquées
Un sémaphore est un tas de cailloux avec deux opérations :
Manipuler des objets de synchronisation, pas des activités
Prendre un caillou, en attendant si nécessaire qu’il y en ait ;
Définition Déposer un caillou.
Un sémaphore encapsule un entier, avec une contrainte de
Attention :
positivité, et deux opérations atomiques d’incrémentation et de
décrémentation. les cailloux sont anonymes et illimités : une activité peut
déposer un caillou sans en avoir pris ;
Contrainte de positivité : l’entier est toujours positif ou nul.
il n’y a pas de lien entre le caillou déposé et l’activité
opération down : décrémente le compteur s’il est strictement déposante ;
positif ; bloque s’il est nul en attendant de pouvoir le
lorsqu’une activité dépose un caillou et qu’il existe des
décrémenter.
activités en attente, une seule d’entre elles peut prendre un
opération up : incrémente le compteur. caillou.
Systèmes concurrents – Sémaphore 7 / 33 Systèmes concurrents – Sémaphore 8 / 33

Spécification Définition Spécification Définition


Applications Spécification intuitive Applications Spécification intuitive
Implantation Spécification formelle : Hoare Implantation Spécification formelle : Hoare
Conclusion Ordonnancement Conclusion Ordonnancement

Spécification formelle : Hoare Propriétés

Si la précondition de S.down() n’est pas vérifiée, l’activité est


Définition retardée ou bloquée.
Un sémaphore S encapsule un entier cnt tel que Quand une activité, via l’opération up, rend vraie la
précondition de S.down() et qu’il existe au moins une activité
init ⇒ S.cnt ≥ 0 bloquée sur down, une telle activité est débloquée (et
{S.cnt = k ∧ k > 0} S.down() {S.cnt = k − 1} décrémente le compteur).
{S.cnt = k} S.up() {S.cnt = k + 1} invariant S.cnt = S.cntinit + #up − #down
où #up et #down sont le nombre d’opérations up et down
effectuées.

Systèmes concurrents – Sémaphore 9 / 33 Systèmes concurrents – Sémaphore 10 / 33


Spécification Définition Spécification Définition
Applications Spécification intuitive Applications Spécification intuitive
Implantation Spécification formelle : Hoare Implantation Spécification formelle : Hoare
Conclusion Ordonnancement Conclusion Ordonnancement

Producteurs/consommateurs simplifié Producteurs/consommateurs

Producteur Consommateur
Case case := nil // tampon partagé
Producteur Consommateur Sémaphores : vide := 1, plein := 0

Consommateur déposer(item) retirer(*item)

échange des données via une unique case (zone mémoire vide.down() plein.down()
partagée) { pré : case libre } { pré : case occupée }
nombre indéterminé et dynamique de producteurs case ← item *item ← case
” ” ” ” de consommateurs { post : case occupée } { post : case libre }
plein.up() vide.up()
objectifs : ne pas écraser une case occupée, une unique lecture
consommatrice par case, attendre pour déposer si plein,
attendre pour retirer si vide
Systèmes concurrents – Sémaphore 11 / 33 Systèmes concurrents – Sémaphore 12 / 33

Spécification Définition Spécification Définition


Applications Spécification intuitive Applications Spécification intuitive
Implantation Spécification formelle : Hoare Implantation Spécification formelle : Hoare
Conclusion Ordonnancement Conclusion Ordonnancement

Ordonnancement

Plusieurs stratégies de déblocage :


Autres noms des opérations : First-In-First-Out = par ordre chronologique d’arrivée
P Probeer down wait/attendre acquire/prendre Priorité des activités demandeuses
(essayer [de décrémenter]) Indéfinie
V Verhoog up signal(er) release/libérer
Les implantations courantes sont en général sans stratégie définie :
(incrémenter)
avec une primitive rapide mais non équitable, on peut implanter
(laborieusement) une solution équitable, mais avec une primitive
lente et équitable, on ne peut pas implanter une solution rapide (et
inéquitable).

Systèmes concurrents – Sémaphore 13 / 33 Systèmes concurrents – Sémaphore 14 / 33


Spécification Définition Spécification Méthodologie
Applications Spécification intuitive Applications L’exclusion mutuelle
Implantation Spécification formelle : Hoare Implantation L’allocateur de ressources critiques
Conclusion Ordonnancement Conclusion Producteurs/consommateurs

Variante : down non bloquant Plan


1 Spécification
Définition
Spécification intuitive
opération tryDown Spécification formelle : Hoare
Ordonnancement
   
(k > 0 ∧ S.cnt = k − 1 ∧ r ) 2 Applications
S.cnt = k r ← S.tryDown()
∨(k = 0 ∧ S.cnt = k ∧ ¬r ) Méthodologie
L’exclusion mutuelle
L’allocateur de ressources critiques
Conduit à de l’attente active.
Producteurs/consommateurs
Généralement utilisé à tort.
3 Implantation
Sémaphore général à partir de verrous
Système d’exploitation
L’inversion de priorité
Systèmes concurrents – Sémaphore 15 / 33 Systèmes concurrents – Sémaphore 16 / 33

Spécification Méthodologie Spécification Méthodologie


Applications L’exclusion mutuelle Applications L’exclusion mutuelle
Implantation L’allocateur de ressources critiques Implantation L’allocateur de ressources critiques
Conclusion Producteurs/consommateurs Conclusion Producteurs/consommateurs

Schémas standards Schémas moins standards

Contrôle du degré de parallélisme Rendez-vous entre deux activités


sémaphore accès := N sémaphore aArrivé := 0
sémaphore bArrivé := 0
accès.down() zone(s) contr^
olée(s) accès.up()

Activité A Activité B
Événements .
. .
.
. .
Attendre/signaler un événement :
aArrivé.up() bArrivé.up()
associer un sémaphore à l’événement : occurrenceE bArrivé.down() aArrivé.down()
(généralement initialisé à 0) .
. .
.
. .
signaler la présence de l’événement : occurrenceE.up()
attendre et consommer une occurrence de l’événement :
occurrenceE.down()

Systèmes concurrents – Sémaphore 17 / 33 Systèmes concurrents – Sémaphore 18 / 33


Spécification Méthodologie Spécification Méthodologie
Applications L’exclusion mutuelle Applications L’exclusion mutuelle
Implantation L’allocateur de ressources critiques Implantation L’allocateur de ressources critiques
Conclusion Producteurs/consommateurs Conclusion Producteurs/consommateurs

Schémas moins standards Pré/post-conditions

Précondition
Précondition de l’action qui suit =
Rendez-vous à N activités (barrière)
état qui doit être vrai pour pouvoir faire l’action,
Pour passer la barrière, une activité doit attendre que les N − 1
autres activités l’aient atteinte. ou événement qui doit être survenu pour pouvoir faire l’action.
sém.down
barrière = array 0..N-1 of Semaphore := {0,...};
Postcondition
-- Protocole de passage pour l’activité k Postcondition de l’action précédente
for i := 0..N-1 do barrière[k].up(); end;
for i := 0..N-1 do barrière[i].down(); end; état garanti après terminaison de l’action,
ou événement qui survient après l’action.
sém.up

Systèmes concurrents – Sémaphore 19 / 33 Systèmes concurrents – Sémaphore 20 / 33

Spécification Méthodologie Spécification Méthodologie


Applications L’exclusion mutuelle Applications L’exclusion mutuelle
Implantation L’allocateur de ressources critiques Implantation L’allocateur de ressources critiques
Conclusion Producteurs/consommateurs Conclusion Producteurs/consommateurs

L’exclusion mutuelle Sémaphore booléen – Verrou

Définition
Sémaphore S encapsulant un booléen b tel que
Algorithme
mutex : global semaphore := 1; {S.b} S.down() {¬S.b}
{true} S.up() {S.b}
mutex.down()
section critique Un sémaphore booléen est différent d’un sémaphore entier
mutex.up() initialisé à 1 : plusieurs up consécutifs sont équivalents à un
seul.
Souvent nommé verrou/lock
Opérations down/up = lock/unlock ou acquire/release

Systèmes concurrents – Sémaphore 21 / 33 Systèmes concurrents – Sémaphore 22 / 33


Spécification Méthodologie Spécification Méthodologie
Applications L’exclusion mutuelle Applications L’exclusion mutuelle
Implantation L’allocateur de ressources critiques Implantation L’allocateur de ressources critiques
Conclusion Producteurs/consommateurs Conclusion Producteurs/consommateurs

Verrou lecture/écriture Allocateur (simplifié) de ressources

Définition
Une ressource peut être utilisée :
N ressources critiques, équivalentes, réutilisables
concurremment par plusieurs lecteurs (plusieurs lecteurs
usage exclusif des ressources
simultanément) ;
opération allouer une ressource
exclusivement par un rédacteur (pas d’autre rédacteur, pas
d’autre lecteur). opération libérer une ressource précédemment obtenue
Souvent rencontré sous la forme de verrou lecture/écriture bon comportement d’une activité : pas deux demandes
(read-write lock). d’allocation consécutives sans libération intermédiaire
Permet l’isolation des modifications avec un meilleur parallélisme ⇒ trivialement implanté par un sémaphore avec N jetons
que l’exclusion mutuelle.
Attention : le problème où allouer demande plusieurs ressources
est plus dur ! Idem, si plusieurs allocations consécutives.

Systèmes concurrents – Sémaphore 23 / 33 Systèmes concurrents – Sémaphore 24 / 33

Spécification Méthodologie Spécification Méthodologie


Applications L’exclusion mutuelle Applications L’exclusion mutuelle
Implantation L’allocateur de ressources critiques Implantation L’allocateur de ressources critiques
Conclusion Producteurs/consommateurs Conclusion Producteurs/consommateurs

Producteurs/consommateurs
0 N−1
Consommateur cons prod
Producteur
déposer(l) retirer(*l)

Producteur tampon Consommateur


vide.down() plein.down()
borné
{ pré : ∃ places libres } { pré : ∃ places occupées }
Consommateur mutex.down() mutex.down()
{ pré : section critique } { pré : section critique }
tampon de taille borné et fixé tampon[prod] ← l *l ← tampon[cons]
nombre indéterminé et dynamique de producteurs prod ← prod + 1 mod N cons ← cons + 1 mod N
{ post : fin SC } { post : fin SC }
” ” ” ” de consommateurs mutex.up() mutex.up()
objectifs : ne pas écraser une case occupée, une unique lecture { post : ∃ places occupées } { post : ∃ places libres }
consommatrice par case, attendre pour déposer si plein, plein.up() vide.up()
attendre pour retirer si vide
Sémaphores : mutex := 1, vide := N, plein := 0
Systèmes concurrents – Sémaphore 25 / 33 Systèmes concurrents – Sémaphore 26 / 33
Spécification Sémaphore général à partir de verrous Spécification Sémaphore général à partir de verrous
Applications Système d’exploitation Applications Système d’exploitation
Implantation L’inversion de priorité Implantation L’inversion de priorité
Conclusion Conclusion

Plan Sémaphore général à partir de verrous


1 Spécification Algorithme
Définition
Sg = hcnt := ?,
Spécification intuitive
mutex := BinarySemaphore(true),
Spécification formelle : Hoare accès := BinarySemaphore(cnt > 0)i // verrous
Ordonnancement Sg.down() = Sg.accès.lock
2 Applications Sg.mutex.lock
Méthodologie S.cnt ← S.cnt - 1
L’exclusion mutuelle si S.cnt ≥ 1 alors Sg.accès.unlock
L’allocateur de ressources critiques Sg.mutex.unlock
Producteurs/consommateurs Sg.up() = Sg.mutex.lock
S.cnt ← S.cnt + 1
3 Implantation si S.cnt = 1 alors Sg.accès.unlock
Sémaphore général à partir de verrous Sg.mutex.unlock
Système d’exploitation
L’inversion de priorité → les sémaphores binaires ont (au moins) la même puissance
d’expression que les sémaphores généraux
Systèmes concurrents – Sémaphore 27 / 33 Systèmes concurrents – Sémaphore 28 / 33

Spécification Sémaphore général à partir de verrous Spécification Sémaphore général à partir de verrous
Applications Système d’exploitation Applications Système d’exploitation
Implantation L’inversion de priorité Implantation L’inversion de priorité
Conclusion Conclusion

Implantation d’un sémaphore Algorithme


S.down() = entrer en excl. mutuelle
si S.nbjetons = 0 alors
insérer self dans S.bloquées
Gestion des activités fournissant : suspendre l’activité courante
sinon
l’exclusion mutuelle (cf partie II) S.nbjetons ← S.nbjetons - 1
le blocage (suspension) et déblocage (reprise) des activités finsi
sortir d’excl. mutuelle
Implantation S.up() = entrer en excl. mutuelle
Sémaphore = h int nbjetons ; si S.bloquées 6= vide alors
actRéveillée ← extraire de S.bloquées
File<Activité> bloquées i
débloquer actRéveillée
sinon
S.nbjetons ← S.nbjetons + 1
finsi
sortir d’excl. mutuelle

Systèmes concurrents – Sémaphore 29 / 33 Systèmes concurrents – Sémaphore 30 / 33


Spécification Sémaphore général à partir de verrous Spécification Sémaphore général à partir de verrous
Applications Système d’exploitation Applications Système d’exploitation
Implantation L’inversion de priorité Implantation L’inversion de priorité
Conclusion Conclusion

Sémaphores et priorités Solution à l’inversion de priorité

Plafonnement de priorité (priority ceiling) : monter


systématiquement la priorité d’une activité verrouilleuse à la
Temps-réel ⇒ priorité ⇒ sémaphore non-FIFO.
priorité maximale des activités potentiellement utilisatrices de
Inversion de priorités : une activité moins prioritaire bloque
cette ressource.
indirectement une activité plus prioritaire.
− Nécessite de connaı̂tre a priori les demandeurs
réveil s.down() − Augmente la priorité même en absence de conflit
Priorité 100 + Simple et facile à implanter
réveil fin + Prédictible : la priorité est associée au sémaphore (= à la
Priorité 50 ressource)
Priorité 10 Héritage de priorité : monter dynamiquement la priorité d’une
s.down() s.up() activité verrouilleuse à celle du demandeur.
+ Limite les cas d’augmentation de priorité aux cas de conflit
− Nécessite de connaı̂tre les possesseurs d’un sémaphore
− Dynamique ⇒ comportement moins prédictible

Systèmes concurrents – Sémaphore 31 / 33 Systèmes concurrents – Sémaphore 32 / 33

Spécification
Applications
Implantation
Conclusion

Conclusion

Le concept de sémaphore est


+ simple
+ facile à comprendre
+ performant
− délicat à l’usage
→ schémas génériques

Systèmes concurrents – Sémaphore 33 / 33


Propriétés Propriétés
L’allocation de ressources multiples L’allocation de ressources multiples
L’interblocage L’interblocage
Conclusion Conclusion

Contenu de cette partie

Quatrième partie Notion d’interblocage


Caractérisation des situations d’interblocage
Protocoles de traitement de l’interblocage
L’interblocage préventifs
curatifs
Apport déterminant d’une bonne modélisation/formalisation
pour la recherche de solutions

Systèmes concurrents 2 / 27 Systèmes concurrents – Interblocage 3 / 27

Propriétés Sûreté / vivacité Propriétés Sûreté / vivacité


L’allocation de ressources multiples La vivacité L’allocation de ressources multiples La vivacité
L’interblocage La famine L’interblocage La famine
Conclusion Conclusion

Plan Propriétés temporelles

1 Propriétés
Sûreté / vivacité
La vivacité Pour toutes les exécutions possibles,
La famine à tout moment d’une exécution.

2 L’allocation de ressources multiples sûreté : rien de mauvais ne se produit


l’exclusion mutuelle, les invariants d’un programme
3 L’interblocage vivacité : quelque chose de bon finit par se produire
Graphe d’allocation l’équité, l’absence de famine, la terminaison d’une boucle
Prévention
(possibilité : pour certaines exécutions, . . . )
Détection
Guérison
4 Conclusion

Systèmes concurrents – Interblocage 4 / 27 Systèmes concurrents – Interblocage 5 / 27


Propriétés Sûreté / vivacité Propriétés Sûreté / vivacité
L’allocation de ressources multiples La vivacité L’allocation de ressources multiples La vivacité
L’interblocage La famine L’interblocage La famine
Conclusion Conclusion

La vivacité La famine (privation)

Progression : si des activités déposent des requêtes de manière


continue, une des requêtes finira par être satisfaite ;
Vivacité faible : si une activité dépose sa requête de manière
continue, elle finira par être satisfaite ; Définition
Une activité est en famine lorsqu’elle attend infiniment longtemps
Vivacité forte : si une activité dépose une requête infiniment
la satisfaction de sa requête (elle n’est jamais satisfaite).
souvent, elle finira par être satisfaite ;
Vivacité FIFO : si une activité dépose une requête, elle sera Vivacité → absence de famine → absence d’interblocage
satisfaite avant tout autre requête (conflictuelle) déposée
ultérieurement.
Facile mais peu performante en centralisé, difficile en réparti.
(les activités sont supposées se comporter  correctement )


Systèmes concurrents – Interblocage 6 / 27 Systèmes concurrents – Interblocage 7 / 27

Propriétés Propriétés
L’allocation de ressources multiples L’allocation de ressources multiples
L’interblocage L’interblocage
Conclusion Conclusion

Plan Allocation de ressources multiples

1 Propriétés
Sûreté / vivacité Ressources banalisées, réutilisables, regroupées en classes
La vivacité Une activité demande un certain nombre de ressources dans
La famine chaque classe
Demande bloquante, ressources allouées par un gérant de
2 L’allocation de ressources multiples
ressources
3 L’interblocage Interface du gérant :
Graphe d’allocation demander : (IdClasse → natural) → (Set of IdRessource)
Prévention libérer : (Set of IdRessource) → unit
Détection Le gérant :
Guérison rend la ressource réutilisable, lors d’une libération ;
libère les ressources détenues, à la terminaison d’une activité.
4 Conclusion

Systèmes concurrents – Interblocage 8 / 27 Systèmes concurrents – Interblocage 9 / 27


Propriétés Propriétés Graphe d’allocation
L’allocation de ressources multiples L’allocation de ressources multiples Prévention
L’interblocage L’interblocage Détection
Conclusion Conclusion Guérison

Exemples Plan

Sémaphore 1 Propriétés
Sûreté / vivacité
1 classe, R ressources, demande = 1
La vivacité
La famine
Philosophes
N classes de 1 ressource, Pi demande Ri et Ri⊕1 2 L’allocation de ressources multiples
3 L’interblocage
Allocateur mono-classe Graphe d’allocation
1 classe de R ressources, demande ∈ [1..R] Prévention
Détection
Lecteurs/rédacteurs Guérison
1 classe de N ressources, demande = 1 ou = N 4 Conclusion

Systèmes concurrents – Interblocage 10 / 27 Systèmes concurrents – Interblocage 11 / 27

Propriétés Graphe d’allocation Propriétés Graphe d’allocation


L’allocation de ressources multiples Prévention L’allocation de ressources multiples Prévention
L’interblocage Détection L’interblocage Détection
Conclusion Guérison Conclusion Guérison

L’interblocage Définition de l’interblocage


Allocation de ressources réutilisables
non réquisitionnables
non partageables Interblocage : définition
en quantités entières et finies Un ensemble d’activités est en interblocage (deadlock) si et
uniques ou multiples seulement si toute activité de l’ensemble est en attente d’une
ressource qui ne peut être libérée que par une autre activité de
Problème : A1 demande R1 puis demande R2 , l’ensemble.
A2 demande R2 puis demande R1
entrelacement → risque d’interblocage.
L’interblocage est un état stable.
1 A demande et obtient R
1 1
2 A demande et obtient R
2 2
3 A demande R et se bloque
1 2
4 A demande R et se bloque
2 1
Systèmes concurrents – Interblocage 12 / 27 Systèmes concurrents – Interblocage 13 / 27
Propriétés Graphe d’allocation Propriétés Graphe d’allocation
L’allocation de ressources multiples Prévention L’allocation de ressources multiples Prévention
L’interblocage Détection L’interblocage Détection
Conclusion Guérison Conclusion Guérison

Graphe d’allocation Conditions nécessaires à un interblocage

A Activité R Ressource 1 Les ressources sont utilisées en exclusion mutuelle


2 Les activités demandent plusieurs ressources en plusieurs fois :
pas de libération nécessaire avant une nouvelle demande
A R A R
blocage tant que la requête ne peut pas être satisfaite
A est en attente de R A possède R 3 Ressources non réquisitionnables
4 Attente circulaire (cycle dans le graphe d’allocation)
Interblocage ≡ cycle/knot dans le graphe d’allocation
Solutions
A1 A1 A3
Prévention : empêcher l’une des quatre conditions de survenir
R1 R2 R1 R2
Détection + guérison : détecter l’interblocage, et l’éliminer
A2 A2

Systèmes concurrents – Interblocage 14 / 27 Systèmes concurrents – Interblocage 15 / 27

Propriétés Graphe d’allocation Propriétés Graphe d’allocation


L’allocation de ressources multiples Prévention L’allocation de ressources multiples Prévention
L’interblocage Détection L’interblocage Détection
Conclusion Guérison Conclusion Guérison

Prévention Prévention

Éviter la non-réquisition
Éviter l’accès exclusif Avant de demander de nouvelles ressources, l’activité est tenue de
Ressource virtuelle : imprimante, fichier libérer toutes les ressources dont elle dispose (quitte à les remettre
dans la nouvelle demande).
Éviter la redemande bloquante (La libération de toutes les ressources n’est en réalité nécessaire
Allocation globale : demander en une seule fois toutes les que si la demande de l’activité est bloquante)
ressources nécessaires
connaissances a priori des ressources nécessaires Éviter l’attente circulaire : classes ordonnées
sur-allocation et risque de famine Un ordre est défini sur les classes de ressources
Acquisition non bloquante : le demandeur peut ajuster sa Toute activité doit demander les ressources selon cet ordre
demande
Solution utilisée pour éviter l’interblocage dû à des verrous
multiples.
Systèmes concurrents – Interblocage 16 / 27 Systèmes concurrents – Interblocage 17 / 27
Propriétés Graphe d’allocation Propriétés Graphe d’allocation
L’allocation de ressources multiples Prévention L’allocation de ressources multiples Prévention
L’interblocage Détection L’interblocage Détection
Conclusion Guérison Conclusion Guérison

Exemple : Philosophes et spaghettis – Dijkstra Exemple : philosophes et interblocage


N philosophes sont autour d’une table. Risque d’interblocage
Il y a une assiette par philosophe, et Chaque philosophe demande sa fourchette gauche et l’obtient. Puis
une fourchette entre chaque assiette. quand tous ont leur fourchette gauche, chaque philosophe
Pour manger, un philosophe doit demande sa fourchette droite et se bloque. ⇒ interblocage
utiliser les deux fourchettes adjacentes
à son assiette (et celles-là seulement). Solutions
Allocation globale : chaque philosophe demande simultanément les
Un philosophe peut être :
deux fourchettes.
penseur : il n’utilise pas de fourchettes ;
Non conservation : quand un philosophe essaye de prendre sa
mangeur : il utilise les deux fourchettes adjacentes ; aucun de
seconde fourchette et qu’elle est déjà prise, il relâche
ses voisins ne peut manger ;
la première et se met en attente sur la seconde.
demandeur : il souhaite manger mais ne dispose pas des deux
Classes ordonnées : imposer un ordre sur les fourchettes ≡ tous les
fourchettes.
philosophes prennent d’abord la gauche puis la
Ce problème est analogue au problème de l’allocateur multi-classes droite, sauf un qui prend d’abord droite puis gauche.
multi-ressources.
Systèmes concurrents – Interblocage 18 / 27 Systèmes concurrents – Interblocage 19 / 27

Propriétés Graphe d’allocation Propriétés Graphe d’allocation


L’allocation de ressources multiples Prévention L’allocation de ressources multiples Prévention
L’interblocage Détection L’interblocage Détection
Conclusion Guérison Conclusion Guérison

Esquive
Avant toute allocation, évaluation du risque (futur) d’interblocage.
12 ressources, A0 /A1 /A2 annoncent 10/4/9
L’algorithme du banquier
Chaque activité annonce le nombre maximal de ressources max poss. dem
qu’elle possédera. A0 10 5
L’algorithme maintient le système dans un état fiable, i.e. tel A1 4 2 +1 oui
qu’il existe toujours une possibilité d’éviter l’interblocage dans (5 + 4 + 2 ≤ 12) ∧ (10 + 2 ≤ 12) ∧ (9 ≤ 12)
le pire des cas. A2 9 2 +1 non
(10 + 2 + 3 > 12)
En cas de danger détecté, le requête est mise en attente
∧(5 + 2 + 9 > 12)
(comme si les ressources n’étaient pas disponibles).
∧((5 + 4 + 3 ≤ 12) ∧ (10 + 3 > 12) ∧ (5 + 9 > 12))
Un état est fiable si pour toute activité Ai , les ressources que
Ai pourra demander peuvent être satisfaites avec les
ressources actuellement disponibles + les ressources détenues
par les Aj , j < i.
Systèmes concurrents – Interblocage 20 / 27 Systèmes concurrents – Interblocage 21 / 27
Propriétés Graphe d’allocation Propriétés Graphe d’allocation
L’allocation de ressources multiples Prévention L’allocation de ressources multiples Prévention
L’interblocage Détection L’interblocage Détection
Conclusion Guérison Conclusion Guérison

Détection Guérison

Réquisition des ressources détenues par une (ou plusieurs)


Construire le graphe d’allocation activité(s).
Détecter l’existence d’un cycle (ressources uniques) ou d’un Comment choisir le(s) activités victimes (la dernière qui a
 knot  (ressources multiples équivalentes) alloué, la plus grosse/petite consommatrice, notion
knot = composante fortement connexe terminale d’importance. . . ) ?
= ensemble S d’activités/ressources tels que
Annulation de l’activité ou retour en arrière ?
∀s ∈ S : successeur(s) ∈ S
∧ s ∈ activité ⇒ successeur(s) 6= ∅ Solution coûteuse (détection + choix + travail perdu),
∧ s ∈ ressources ⇒ card(succ(s)) = nb ress. pas toujours acceptable (systèmes interactifs, systèmes
Algorithme coûteux → périodiquement (et non à chaque embarqués).
allocation) Simplifie l’allocation.
Points de reprise pour retour arrière.

Systèmes concurrents – Interblocage 22 / 27 Systèmes concurrents – Interblocage 23 / 27

Propriétés Graphe d’allocation Propriétés


L’allocation de ressources multiples Prévention L’allocation de ressources multiples
L’interblocage Détection L’interblocage
Conclusion Guérison Conclusion

Points de reprise Plan

Définition 1 Propriétés
Sauvegarde d’états intermédiaires pour éviter de perdre tout le Sûreté / vivacité
travail. La vivacité
La famine
Utilisé pour les transactions en base de données
Effet domino : l’annulation d’une action entraı̂ne l’annulation d’une 2 L’allocation de ressources multiples
deuxième action qui. . .
4 1 3 L’interblocage
Graphe d’allocation
P1 écriture
x
Point de Annulation Prévention
reprise 5 2 Détection
P2 lecture
Guérison
6 3
4 Conclusion
P3
Systèmes concurrents – Interblocage 24 / 27 Systèmes concurrents – Interblocage 25 / 27
Propriétés Propriétés
L’allocation de ressources multiples L’allocation de ressources multiples
L’interblocage L’interblocage
Conclusion Conclusion

L’interblocage Autres difficultés de synchronisation


Usuellement : interblocage = inconvénient occasionnel Accès non protégé à un état partagé (conflits d’écriture,
→ laissé à la charge de l’utilisateur/du programmeur visibilité d’états transitoires). Se manifestent souvent par des
utilisation de méthodes de prévention simples (p.e. classes
comportements non reproductibles (heisenbugs)
ordonnées)
ou détection empirique et guérison par choix manuel des Variables partagées ⇒ verrous
victimes Outils d’analyse statique pour identifier de potentiels
Cas particuliers : systèmes ouverts (plus ou moins) contraints problèmes
par le temps Invalidation d’un invariant (souvent facile à détecter, mais
systèmes interactifs, multiprocesseurs, systèmes embarqués coûteux et non correctible en général)
recherche de méthodes efficaces, prédictibles, ou automatiques
Famine : difficilement prouvable, impossible à détecter (sauf
compromis/choix à réaliser entre la prévention (statique,
coûteuse, restreint le parallélisme) et détection/guérison stratégie régulière : FIFO)
(moins prédictible, coûteuse quand les conflits sont fréquents).  Livelock  : activité bouclant sans jamais libérer toutes ses
Émergence d’approches sans blocage (cf synchronisation non ressources ou acquisition non bloquante avec retentatives qui
bloquante) échouent toujours.
Systèmes concurrents – Interblocage 26 / 27 Systèmes concurrents – Interblocage 27 / 27
Spécification Spécification
Applications Applications
Variantes Variantes

Contenu de cette partie

Cinquième partie
Un objet de synchronisation structuré : le moniteur
Démarche de conception basée sur l’utilisation de moniteurs
Moniteur Exemple récapitulatif (schéma producteurs/consommateurs)
Variantes

Systèmes concurrents 2 / 31 Systèmes concurrents – Moniteur 3 / 31

Spécification Définition Spécification Définition


Applications Transfert du contrôle exclusif Applications Transfert du contrôle exclusif
Variantes Variantes

Plan Limites des sémaphores

1 Spécification
Définition
Imbrication aspects de synchronisation/aspects fonctionnels
Transfert du contrôle exclusif
→ manque de modularité, code des activités interdépendant
2 Applications Pas de contrainte sur le protocole d’utilisation des sémaphores
Méthodologie → démarche de conception artisanale, à partir de schémas
Producteurs/consommateurs élémentaires (attendre/signaler un événement, contrôler
Allocateur de ressources l’accès à une ressource. . . )
Approche opératoire
3 Variantes → raisonnement et vérification difficiles
Régions critiques
Réveil multiple

Systèmes concurrents – Moniteur 4 / 31 Systèmes concurrents – Moniteur 5 / 31


Spécification Définition Spécification Définition
Applications Transfert du contrôle exclusif Applications Transfert du contrôle exclusif
Variantes Variantes

Définition – Hoare 1974 Synchronisation : variable condition


Idée de base
Un moniteur est une construction qui permet de définir et de Définition
contrôler le bon usage d’un objet partagé par un ensemble Variables de type condition définies dans le moniteur.
d’activités. Opérations possibles sur une condition C :
C .wait : bloque l’activité appelante en libérant l’accès exclusif
Définition
au moniteur.
Un moniteur = un module exportant des procédures (et
C .signal : s’il y a des activités bloquées sur C , en réveille une ;
éventuellement des constantes et des types).
sinon, nop.
Contrainte d’exclusion mutuelle pour l’exécution des
procédures du moniteur : au plus une procédure en cours condition ≈ événement
d’exécution. condition 6= sémaphore (pas de mémorisation des
Mécanisme de synchronisation interne.  signaux )

condition 6= prédicat logique


Un moniteur est passif : ce sont les activités qui invoquent ses
procédures.
Systèmes concurrents – Moniteur 6 / 31 Systèmes concurrents – Moniteur 7 / 31

Spécification Définition Spécification Définition


Applications Transfert du contrôle exclusif Applications Transfert du contrôle exclusif
Variantes Variantes

Exemple élémentaire Exemple élémentaire – Le moniteur

Travail délégué : 1 client + 1 ouvrier variables d’état : req, rés


variables conditions : TravailDéposé, RésultatDispo
Les activités prendre travail(out t)
Client Ouvrier si req = null alors
déposer travail(in t)
TravailDéposé.wait
boucle req ← t
boucle finsi
.
. . TravailDéposé.signal
. .
. t ← req
déposer travail(t) x ← prendre travail() req ← null
.
. // (y ← f (x)) lire résultat(out r)
. si rés = null alors
r ←lire résultat() rendre résultat(y) rendre résultat(in y)
. . RésultatDispo.wait
. .
. rés ← y
. finsi
finboucle RésultatDispo.signal
finboucle r ← rés
rés ← null

Systèmes concurrents – Moniteur 8 / 31 Systèmes concurrents – Moniteur 9 / 31


Spécification Définition Spécification Définition
Applications Transfert du contrôle exclusif Applications Transfert du contrôle exclusif
Variantes Variantes

Transfert du contrôle exclusif Priorité au signalé


Le code dans le moniteur est exécuté en exclusion mutuelle.
Lors d’un réveil par signal, qui obtient/garde l’accès exclusif ?
signaleurs
Priorité au signalé C.signal
Lors du réveil par signal, entrer C.bloqués

l’accès exclusif est transféré à l’activité réveillée (signalée) ; entrants C.wait


...
l’activité signaleuse est mise en attente de pouvoir ré-acquérir
l’accès exclusif.
sortir
Priorité au signaleur C .signal()
Lors du réveil par signal, = opération nulle si pas de bloqués sur C
l’accès exclusif est conservé par l’activité réveilleuse ; sinon,
suspend et ajoute le signaleur à la file des signaleurs
l’activité réveillée (signalée) est mise en attente de pouvoir passe le contrôle à l’activité en tête des bloqués sur C
acquérir l’accès exclusif.
signaleurs prioritaires sur les entrants (progression garantie)
Systèmes concurrents – Moniteur 10 / 31 Systèmes concurrents – Moniteur 11 / 31

Spécification Définition Spécification Définition


Applications Transfert du contrôle exclusif Applications Transfert du contrôle exclusif
Variantes Variantes

Priorité au signaleur, avec file spécifique des signalés Priorité au signaleur, sans file spécifique des signalés
C.signal C.signal

signalés

C.bloqués C.bloqués
entrer entrants C.wait entrer entrants C.wait
... signalés ...

sortir sortir

C .signal() C .signal()
si la file des bloqués sur C est non vide, extrait l’activité de si la file des bloqués sur C est non vide, extrait l’activité de
tête et la range dans la file des signalés tête et la range dans la file des entrants
le signaleur conserve le contrôle le signaleur conserve le contrôle
signalés prioritaires sur les entrants signalés non prioritaires vis-à-vis des entrants
Systèmes concurrents – Moniteur 12 / 31 Systèmes concurrents – Moniteur 13 / 31
Spécification Définition Spécification Définition
Applications Transfert du contrôle exclusif Applications Transfert du contrôle exclusif
Variantes Variantes

Et donc ? Simplifier l’expression de la synchronisation ?


Idée
Attente sur des prédicats, plutôt que sur des événements (variables
Priorité au signalé : garantit que l’activité réveillée obtient de type condition)
l’accès au moniteur dans l’état où il était lors du signal. → opération : wait(B) où B est une expression booléenne
Raisonnement simplifié
Absence de famine facile Exemple : travail délégué, avec wait(prédicat)
Priorité au signaleur : le réveillé obtient le moniteur variables d’état : req, rés
ultérieurement, éventuellement après que d’autres activités – requête/résultat en attente (null si aucun(e))
ont eu accès au moniteur. prendre travail(out t)
Implantation du mécanisme plus simple et plus performante déposer travail(in t) wait(req 6= null)
Nécessité de tester à nouveau la condition de déblocage au req ← t t ← req
réveil du signalé req ← null
Absence de famine/vivacité plus difficile lire résultat(out r)
wait(rés 6= null) rendre résultat(in y)
r ← rés rés ← y
rés ← null
Systèmes concurrents – Moniteur 14 / 31 Systèmes concurrents – Moniteur 15 / 31

Spécification Définition Spécification Méthodologie


Applications Transfert du contrôle exclusif Applications Producteurs/consommateurs
Variantes Variantes Allocateur de ressources

Pourquoi wait(prédicat) n’est-il pas disponible en pratique ? Plan


Efficacité problématique : évaluer B à chaque nouvel état (= à
chaque affectation), et pour chacun des prédicats attendus. 1 Spécification
→ gestion de l’évaluation laissée au programmeur Définition
une variable condition (P valide) est associée à chaque Transfert du contrôle exclusif
prédicat (P)
wait(P) est implantée par 2 Applications
si ¬P alors P valide.wait() fsi Méthodologie
Producteurs/consommateurs
le programmeur a la possibilité de signaler (P valide.signal())
Allocateur de ressources
aux instants/états (pertinents) où P est valide

Principe 3 Variantes
Régions critiques
1 Concevoir en termes de prédicats attendus
Réveil multiple
2 Simuler cette attente de prédicats avec des variables
conditions
Systèmes concurrents – Moniteur 16 / 31 Systèmes concurrents – Moniteur 17 / 31
Spécification Méthodologie Spécification Méthodologie
Applications Producteurs/consommateurs Applications Producteurs/consommateurs
Variantes Allocateur de ressources Variantes Allocateur de ressources

Méthodologie Schéma standard d’une opération

1 Déterminer l’interface du moniteur Si prédicat d’acceptation est faux alors


2 Énoncer informellement les prédicats d’acceptation de chaque Attendre (wait) sur la variable condition associée
opérations finsi
3 Déduire les variables d’état qui permettent d’écrire ces { (1) État nécessaire au bon déroulement }
prédicats d’acceptation Maj de l’état du moniteur (action)
{ (2) État garanti }
4 Formuler l’invariant du moniteur et les prédicats d’acceptation
Signaler (signal) les variables conditions dont le prédicat associé
5 Pour chaque prédicat d’acceptation, définir une variable est vrai
condition
Vérifier, pour chaque variable condition, que chaque précondition à
6 Programmer les opérations
signal (2) implique chaque postcondition de wait (1) de la
7 Vérifier l’invariant et les réveils variable condition correspondante.

Systèmes concurrents – Moniteur 18 / 31 Systèmes concurrents – Moniteur 19 / 31

Spécification Méthodologie Spécification Méthodologie


Applications Producteurs/consommateurs Applications Producteurs/consommateurs
Variantes Allocateur de ressources Variantes Allocateur de ressources

Producteurs/consommateurs Méthodologie appliquée aux producteurs/consommateurs

Producteur Consommateur 1 Interface :


déposer(in v)
Producteur tampon Consommateur retirer(out v)
borné 2 Prédicats d’acceptation :
Consommateur déposer : il y a de la place, le tampon n’est pas plein
retirer : il y a quelque chose, le tampon n’est pas vide
tampon de taille borné et fixé 3 Variables d’état :
nombre indéterminé et dynamique de producteurs nbOccupées : natural
” ” ” ” de consommateurs déposer : nbOccupées < N
retirer : nbOccupées > 0
objectifs : ne pas écraser une case occupée, une unique lecture
consommatrice par case, attendre pour déposer si plein,
4 Invariant : 0 ≤ nbOccupées ≤ N
attendre pour retirer si vide 5 Variables conditions : PasPlein, PasVide

Systèmes concurrents – Moniteur 20 / 31 Systèmes concurrents – Moniteur 21 / 31


Spécification Méthodologie Spécification Méthodologie
Applications Producteurs/consommateurs Applications Producteurs/consommateurs
Variantes Allocateur de ressources Variantes Allocateur de ressources

déposer(in v) Vérification & Priorité


si ¬(nbOccupées < N) alors
PasPlein.wait
finsi Vérification : (2) ⇒ (3) ? (4) ⇒ (1) ?
{ (1) nbOccupées < N }
// action applicative (ranger v dans le tampon)
Si priorité au signaleur, transformer si en tant que :
nbOccupées + + déposer(in v)
{ (2) N ≥ nbOccupées > 0 }
PasVide.signal tant que ¬(nbOccupées < N) faire
PasPlein.wait
retirer(out v) fintq
si ¬(nbOccupées > 0) alors { (1) nbOccupées < N }
PasVide.wait // action applicative (ranger v dans le tampon)
finsi nbOccupées + +
{ (3) nbOccupées > 0 }
{ (2) N ≥ nbOccupées > 0 }
// action applicative (prendre v dans le tampon)
nbOccupées − − PasVide.signal
{ (4) 0 ≤ nbOccupées < N }
PasPlein.signal
Systèmes concurrents – Moniteur 22 / 31 Systèmes concurrents – Moniteur 23 / 31

Spécification Méthodologie Spécification Méthodologie


Applications Producteurs/consommateurs Applications Producteurs/consommateurs
Variantes Allocateur de ressources Variantes Allocateur de ressources

Allocateur de ressources Allocateur de ressources - méthodologie

1 Interface :
demander(p: 1..N)
N ressources équivalentes, une activité en demande p ∈ 1..N libérer(q: 1..N)
puis les libère. 2 Prédicats d’acceptation :
Bon comportement : pas deux demandes consécutives sans demander(p) : il y a au moins p ressources libres
libération (cf interblocage). retirer(q) : rien
Difficulté : une libération peut débloquer 0, 1 ou plusieurs 3 Variables d’état :
demandeurs selon le nombre de ressources rendues et nbDispo : natural
attendues. demander(p) : nbDispo ≥ p
libérer(q) : true
4 Invariant : 0 ≤ nbDispo ≤ N
5 Variable condition : AssezDeRessources

Systèmes concurrents – Moniteur 24 / 31 Systèmes concurrents – Moniteur 25 / 31


Spécification
Allocateur – opérations Applications
Régions critiques
Réveil multiple
Variantes

demander(p)
Plan
si demande 6= 0 alors -- il y a déjà un demandeur → j’attends mon tour
Sas.wait
finsi
si ¬(nbDispo < p) alors
1 Spécification
demande ← p Définition
AssezDeRessources.wait Transfert du contrôle exclusif
demande ← 0
finsi 2 Applications
nbDispo ← nbDispo − p Méthodologie
Sas.signal -- au suivant de demander Producteurs/consommateurs
libérer(q) Allocateur de ressources

nbDispo ← nbDispo + p 3 Variantes


si nbDispo ≥ demande alors Régions critiques
AssezDeRessources.signal
finsi
Réveil multiple

Note : priorité au signaleur ⇒ transformer le premier “si” de


demander en “tant que” (ça suffit). Systèmes concurrents – Moniteur 27 / 31

Spécification Régions critiques Spécification Régions critiques


Applications Réveil multiple Applications Réveil multiple
Variantes Variantes

Régions critiques Régions critiques

Éliminer les variables conditions et les appels explicites à Exemple


signal ⇒ déblocages calculés par le système.
tampon : shared array 0..N-1 of msg;
Exclusion mutuelle plus  fine , en listant les variables nbOcc : shared int := 0;
partagées effectivement utilisées. retrait, dép^
ot : shared int := 0, 0;

region liste des variables utilisées déposer(m) retirer()


when prédicat logique region region
nbOcc, tampon, dép^
ot nbOcc, tampon, retrait
do code
when when
nbOcc < N nbOcc > 0
1 Attente que le prédicat logique soit vrai do do
2 Le code est exécuté en exclusion mutuelle vis-à-vis des autres ot] ← m
tampon[dép^ Result ← tampon[retrait]
régions ayant (au moins) une variable commune ot ← dép^
dép^ ot + 1 % N retrait ← retrait + 1 % N
nbOcc ← nbOcc + 1 nbOcc ← nbOcc - 1
3 À la fin du code, évaluation automatique des prédicats
end end
logiques des régions pour débloquer éventuellement.
Systèmes concurrents – Moniteur 28 / 31 Systèmes concurrents – Moniteur 29 / 31
Spécification Régions critiques Spécification Régions critiques
Applications Réveil multiple Applications Réveil multiple
Variantes Variantes

Réveil multiple : signalAll/broadcast Conclusion

C .signalAll (ou broadcast) : toutes les activités bloquées sur la Un moniteur implante un objet partagé, et contrôle la bonne
variable condition C sont débloquées. Elles se mettent en attente utilisation de cet objet
de l’accès exclusif.
Apports
Rarement utilisé à bon escient. Une solution triviale à un problème modularité et encapsulation.
de synchronisation est d’utiliser une unique variable condition la synchronisation est localisée dans le moniteur →
Accès et d’écrire toutes les procédures du moniteur sous la même raisonnement simplifié
forme : meilleure lisibilité
Limites
tant que ¬(condition d’acceptation) faire
la synchronisation reste mêlée aux aspects fonctionnels
Accès.wait
la sémantique des moniteurs est complexe
fintq
l’exclusion mutuelle sur les opérations facilite la conception,
... mais :
Accès.signalAll -- battez-vous est une source potentielle d’interblocages (moniteurs
imbriqués)
Mauvaise idée ! (performance, prédictibilité) est une limite du point de vue de l’efficacité
Systèmes concurrents – Moniteur 30 / 31 Systèmes concurrents – Moniteur 31 / 31
Généralités Généralités
Threads Java Threads Java
Synchronisation Java Synchronisation Java
POSIX Threads & autres approches POSIX Threads & autres approches

Contenu de cette partie

Préparation aux TPs : présentation des outils de programmation


Sixième partie concurrente autour de la plateforme Java
notion de processus léger
présentation de la plateforme
Programmation multi-activités classe Thread
Java & Posix Threads objets de synchronisation : moniteurs, sémaphores. . .
régulation des activités : pools d’activités, appels asynchrones,
fork/join. . .
outils de synchronisation de bas niveau
autres environnements et modèles : Posix, OpenMP. . .

Systèmes concurrents 2 / 62 Systèmes concurrents – Java & Posix Threads 3 / 62

Généralités Généralités
Threads Java Threads Java
Synchronisation Java Synchronisation Java
POSIX Threads & autres approches POSIX Threads & autres approches

Plan Processus multi-activités


1 Généralités Processus Unix /
Machine Java
2 Threads Java
Manipulation des activités
3 Synchronisation Java
Moniteur Java
Autres objets de synchronisation
Régulation du parallélisme
Synchronisation (java d’origine)
4 POSIX Threads & autres approches
Posix Threads 1 espace d’adressage, plusieurs flots de contrôle.
Synchronisation Posix Thread ⇒ plusieurs activités (ou processus légers) au sein d’un même
Autres approches processus UNIX / d’une même machine virtuelle Java.

Systèmes concurrents – Java & Posix Threads 4 / 62 Systèmes concurrents – Java & Posix Threads 5 / 62
Généralités Généralités
Threads Java Threads Java Manipulation des activités
Synchronisation Java Synchronisation Java
POSIX Threads & autres approches POSIX Threads & autres approches

Plan
Processus Unix
1 Généralités
Fichiers Activité Activité Activité
2 Threads Java
Signaux Registres Registres Registres
(masque, connexion) Manipulation des activités
Données Données Données
spécifiques spécifiques spécifiques 3 Synchronisation Java
Moniteur Java
Mémoire Autres objets de synchronisation
Pile Pile Pile Régulation du parallélisme
Tas
Synchronisation (java d’origine)
Static
4 POSIX Threads & autres approches
Code Posix Threads
Synchronisation Posix Thread
Autres approches

Systèmes concurrents – Java & Posix Threads 6 / 62 Systèmes concurrents – Java & Posix Threads 7 / 62

Généralités Généralités
Threads Java Manipulation des activités Threads Java Manipulation des activités
Synchronisation Java Synchronisation Java
POSIX Threads & autres approches POSIX Threads & autres approches

Conception d’applications parallèles en Java Cycle de vie d’une activité

Java permet de manipuler


exécutable (actif)
les processus (lourds) : classes java.lang.ProcessBuilder
start fin du code
et java.lang.Process créé prêt
yield
on−CPU terminé
les activités (processus légers) : classe java.lang.Thread
Le degré de parallélisme des applications Java peut être notify,
wait
read, accept, ...
interrupt
contrôlé directement (manipulation des threads) join, sleep

ou régulé bloqué bloqué


(synchro ou sleep) (E/S)
explicitement : interface java.util.concurrent.Executor
implicitement : programmation asynchrone/fonctionnelle

Systèmes concurrents – Java & Posix Threads 8 / 62 Systèmes concurrents – Java & Posix Threads 9 / 62
Généralités
Threads Java Manipulation des activités Création d’activités – exemple
Synchronisation Java
POSIX Threads & autres approches

class Compteur implements Runnable {


Création d’une activité 1 private int max;
private int step;
Implantation de l’interface Runnable (méthode run) public Compteur(int max, int step) {
this.max = max; this.step = step;
Définition d’une activité
}
class X implements Runnable { public void run() {
public void run() { /* code du thread */ } for (int i = 0; i < max; i += step)
} System.out.println(i);
}
}
Utilisation
X x = new X(. . . ) ; public class DemoThread {
Thread t = new Thread(x) ; // activité créée public static void main (String[] a) {
t.start() ; // activité démarrée Compteur c2 = new Compteur(10, 2);
.. Compteur c3 = new Compteur(15, 3);
. new Thread(c2).start();
t.join() ; // attente de la terminaison new Thread(c3).start();
}
Systèmes concurrents – Java & Posix Threads 10 / 62 }

Généralités Généralités
Threads Java Manipulation des activités Threads Java Manipulation des activités
Synchronisation Java Synchronisation Java
POSIX Threads & autres approches POSIX Threads & autres approches

Création d’une activité 2 Quelques méthodes


Héritage de la classe Thread et implantation de la méthode run :
Classe Thread :
Définition d’une activité
static Thread currentThread()
class X extends Thread { obtenir l’activité appelante
public void run() { /* code du thread */ }
static void sleep(long ms) throws InterruptedException
}
suspend l’exécution de l’activité appelante pendant la
durée indiquée (ou jusqu’à ce que l’activité soit
Utilisation
interrompue)
X x = new X() ; // activité créée
void join() throws InterruptedException
x.start() ; // activité démarrée
suspend l’exécution de l’activité appelante jusqu’à la
...
terminaison de l’activité sur laquelle join() est
x.join() ; // attente de la terminaison
appliquée (ou jusqu’à ce que l’activité appelante soit
interrompue)
Déconseillé : risque d’erreur de redéfinition de Thread.run.
Systèmes concurrents – Java & Posix Threads 12 / 62 Systèmes concurrents – Java & Posix Threads 13 / 62
Généralités Généralités
Threads Java Manipulation des activités Threads Java Manipulation des activités
Synchronisation Java Synchronisation Java
POSIX Threads & autres approches POSIX Threads & autres approches

Interruption Variables localisées


Mécanisme minimal permettant d’interrompre une activité. Chaque activité possède sa propre valeur associée à un même objet
La méthode interrupt (appliquée à une activité) provoque localisé.
soit la levée de l’exception InterruptedException si l’activité Instances de ThreadLocal ou InheritableThreadLocal.
est bloquée sur une opération de synchronisation class MyValue extends ThreadLocal {
(Thread.join, Thread.sleep, Object.wait) // surcharger éventuellement initValue
soit le positionnement d’un indicateur interrupted, testable par : }
class Common {
boolean isInterrupted() qui renvoie la valeur de static MyValue val = new MyValue();
l’indicateur de l’activité sur laquelle cette }
méthode est appliquée ;
static boolean interrupted() qui renvoie et efface la thread t1 thread t2
valeur de l’indicateur de l’activité appelante. o = new Integer(1); o = "machin";
Common.val.set(o); Common.val.set(o);
Pas d’interruption des entrées-sorties bloquantes ⇒ peu utile. x = Common.val.get(); x = Common.val.get();

Systèmes concurrents – Java & Posix Threads 14 / 62 Systèmes concurrents – Java & Posix Threads 15 / 62

Généralités Moniteur Java Généralités Moniteur Java


Threads Java Autres objets de synchronisation Threads Java Autres objets de synchronisation
Synchronisation Java Régulation du parallélisme Synchronisation Java Régulation du parallélisme
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches Synchronisation (java d’origine)

Plan Objets de synchronisation


1 Généralités
Le paquetage java.util.concurrent fournit
2 Threads Java une réalisation des moniteurs
Manipulation des activités
divers autres objets de synchronisation
3 Synchronisation Java barrière
Moniteur Java sémaphore
Autres objets de synchronisation compteur
Régulation du parallélisme ...
Synchronisation (java d’origine) le contrôle du degré de parallélisme : Thread, Executor
4 POSIX Threads & autres approches des structures de données autorisant/facilitant les accès
concurrents
Posix Threads
accès atomiques : ConcurrentHashMap. . .
Synchronisation Posix Thread accès non bloquants : ConcurrentLinkedQueue
Autres approches

Systèmes concurrents – Java & Posix Threads 16 / 62 Systèmes concurrents – Java & Posix Threads 17 / 62
Généralités Moniteur Java
Threads Java Autres objets de synchronisation
Régulation du parallélisme
Moniteur Java
Synchronisation Java
POSIX Threads & autres approches Synchronisation (java d’origine)

Moniteur Java import java.util.concurrent.locks.*;


class ProdCon {
Lock verrou = new ReentrantLock();
Condition pasPlein = verrou.newCondition();
Condition pasVide = verrou.newCondition();
Principe des moniteurs Object[] items = new Object[100];
int depot, retrait, nbElems;
1 verrou assurant l’exclusion mutuelle
plusieurs variables conditions associées à ce verrou public void deposer(Object x) throws InterruptedException {
verrou.lock();
attente/signalement de ces variables conditions
while (nbElems == items.length) pasPlein.await();
= un moniteur items[depot] = x;
pas de priorité au signalé et pas de file des signalés depot = (depot + 1) % items.length;
nbElems++;
pasVide.signal();
verrou.unlock();
}
.
.
.
Systèmes concurrents – Java & Posix Threads 18 / 62

Généralités Moniteur Java Généralités Moniteur Java


Threads Java Autres objets de synchronisation Threads Java Autres objets de synchronisation
Synchronisation Java Régulation du parallélisme Synchronisation Java Régulation du parallélisme
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches Synchronisation (java d’origine)

Sémaphores, producteurs/consommateurs Barrière


Paquetage java.util.concurrent
java.util.concurrent.CyclicBarrier
Sémaphore Rendez-vous bloquant entre N activités : passage bloquant tant
Semaphore s = new Semaphore(1); // nb init. de jetons que les N activités n’ont pas demandé à franchir la barrière ;
s.acquire(); // = down passage autorisé pour toutes quand la N-ième arrive.
s.release(); // = up
CyclicBarrier barrier = new CyclicBarrier(3);
for (int i = 0; i < 8; i++) {
BlockingQueue Thread t = new Thread() {
BlockingQueue = producteurs/consommateurs (interface) public void run() {
LinkedBlockingQueue = prod./cons. à tampon non borné barriere.await();
System.out.println("Passé !");
ArrayBlockingQueue = prod./cons. à tampon borné }};
t.start();
BlockingQueue bq = new ArrayBlockingQueue(4); // capacité }
bq.put(m); // dép^
ot (bloquant) d’un objet en queue
Généralisation : la classe Phaser permet un rendez-vous (bloquant
x = bq.take(); // obtention (bloquante) de l’objet en t^ete
ou non) pour un groupe variable d’activités.
Systèmes concurrents – Java & Posix Threads 20 / 62 Systèmes concurrents – Java & Posix Threads 21 / 62
Généralités Moniteur Java Généralités Moniteur Java
Threads Java Autres objets de synchronisation Threads Java Autres objets de synchronisation
Synchronisation Java Régulation du parallélisme Synchronisation Java Régulation du parallélisme
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches Synchronisation (java d’origine)

Compteurs, Verrous L/R Atomicité à grain fin


java.util.concurrent.countDownLatch Outils pour réaliser la coordination par l’accès à des données
partagées, plutôt que par suspension/réveil (attente/signal
init(N) valeur initiale du compteur
d’événement)
await() bloque si strictement positif, rien sinon. le paquetage java.util.concurrent.atomic fournit des
countDown() décrémente (si strictement positif). classes qui permettent des accès atomiques cohérents,
Lorsque le compteur devient nul, toutes les activités et des opérations de mise à jour conditionnelle du type
bloquées sont débloquées. TestAndSet.
Les lectures et écritures des références déclarées volatile
interface java.util.concurrent.locks.ReadWriteLock sont atomiques et cohérentes.
Verrous pouvant être acquis en mode ⇒ synchronisation non bloquante
exclusif (writeLock().lock()), Danger
partagé avec les autres non exclusifs (readLock().lock()) Concevoir et valider de tels algorithmes est très ardu. Ceci a
→ schéma lecteurs/rédacteurs. motivé la définition d’objets de synchronisation (sémaphores,
Implantation : ReentrantReadWriteLock (avec/sans équité) moniteurs. . . ) et de patrons (producteurs/consommateurs. . . )
Systèmes concurrents – Java & Posix Threads 22 / 62 Systèmes concurrents – Java & Posix Threads 23 / 62

Généralités Moniteur Java Généralités Moniteur Java


Threads Java Autres objets de synchronisation Threads Java Autres objets de synchronisation
Synchronisation Java Régulation du parallélisme Synchronisation Java Régulation du parallélisme
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches Synchronisation (java d’origine)

Services de régulation du parallélisme : exécuteurs Interfaces d’exécuteurs

L’interface java.util.concurrent.Executor définit la


méthode
Idée void execute(Runnable r),
Séparer la création et la gestion des activités des autres aspects fonctionnellement équivalente à (new Thread(r)).start(),
(fonctionnels, synchronisation. . . ) avec la différence que r ne sera pas forcément exécutée
→ définition d’un service de gestion des activités (exécuteur), immédiatement/par un thread spécifique.
régulant/adaptant le nombre de threads effectivement actifs, L’interface ExecutorService permet de soumettre (méthode
en fonction de la charge courante et du nombre de processeurs submit(...)) des tâches rendant un résultat (Callable),
physiques disponibles : lequel pourra être récupéré par la suite, de manière
trop de threads → consommation de ressources inutile asynchrone.
pas assez de threads → capacité de calcul sous-utilisée
L’interface ScheduledExecutorService est un
ExecutorService, avec la possibilité de spécifier un
calendrier (départs, périodicité. . . ) pour les tâches exécutées.

Systèmes concurrents – Java & Posix Threads 24 / 62 Systèmes concurrents – Java & Posix Threads 25 / 62
Généralités Moniteur Java
Exemple d’utilisation d’un Executor Threads Java Autres objets de synchronisation
Régulation du parallélisme
Synchronisation Java
POSIX Threads & autres approches Synchronisation (java d’origine)

import java.util.concurrent.*;
public class ExecutorExample { Pool de Threads
public static void main(String[] a) throws Exception {
final int NB = 10;
Schéma de base pour la plupart des implémentations d’exécuteurs
ExecutorService exec = Executors.newCachedThreadPool(); Une file d’attente de travaux à effectuer
Future<?>[] res = new Future<?>[NB]; Un ensemble (fixe ou dynamique) d’activités (ouvriers)
Une politique de distribution des travaux aux activités
// lancement des travaux (réalisée par un protocole ou par une activité)
for (int i = 0; i < NB; i++) {
int j = i; Q (file de travaux)
exec.execute(() -> { System.out.println("hello"+j); }); Q.put()
P.execute(j) execute()
res[i] = exec.submit(() -> { return 3*j; });
}

// récupération des résultats


j=Q.take() j=Q.take() j=Q.take()
for (int i = 0; i < NB; i++) { j.run() j.run() j.run()
System.out.println(res[i].get()); ... ...
} ouvrier ouvrier ouvrier
}
Pool P [sans politique de distribution particulière (file partagée)]
} Systèmes concurrents – Java & Posix Threads 27 / 62

Généralités Moniteur Java


Thread pool naı̈f Threads Java Autres objets de synchronisation
Régulation du parallélisme
Synchronisation Java
POSIX Threads & autres approches Synchronisation (java d’origine)
import java.util.concurrent.*;
public class NaiveThreadPool2 implements Executor { Exécuteurs prédéfinis
private BlockingQueue<Runnable> queue;
public NaiveThreadPool2(int nthr) { java.util.concurrent.Executors est une fabrique pour des
queue = new LinkedBlockingQueue<Runnable>(); stratégies d’exécution :
for (int i=0; i<nthr; i++) { (new Worker()).start(); } Nombre fixe d’activités : newSingleThreadExecutor(),
}
newFixedThreadPool(int nThreads)
Nombre d’activités adaptable : newCachedThreadPool()
public void execute(Runnable job) {
Quand il n’y a plus d’activité disponible et qu’un travail est
queue.put(job);
déposé, création d’une nouvelle activité
}
Quand la queue est vide et qu’un délai suffisant (p.ex. 1mn)
s’est écoulé, terminaison d’une activité inoccupée
private class Worker extends Thread {
public void run() { Parallélisme massif avec vol de jobs :
while (true) { newWorkStealingPool(int parallelism)
Runnable job = queue.take(); // bloque si nécessaire java.util.concurrent.ThreadPoolExecutor permet de
job.run(); contrôler l’ensemble des paramètres de la stratégie d’exécution :
}
politique de la file (FIFO, priorités. . . ), file bornée ou non bornée,
}}
}
nombre de threads minimum, maximum. . .
Systèmes concurrents – Java & Posix Threads 29 / 62
Généralités Moniteur Java Généralités Moniteur Java
Threads Java Autres objets de synchronisation Threads Java Autres objets de synchronisation
Synchronisation Java Régulation du parallélisme Synchronisation Java Régulation du parallélisme
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches Synchronisation (java d’origine)

Évaluation asynchrone Schéma diviser pour régner (fork/join, map/reduce)


Evaluation paresseuse : l’appel effectif d’une fonction peut être
différée (éventuellement exécutée en parallèle avec l’appelant)
submit(...) fournit à l’appelant une référence à la valeur Schéma de base
future du résultat. Résultat résoudre(Problème pb) {
L’appelant ne se bloque que quand il doit utiliser le résultat si (pb est assez petit) {
de l’appel (si l’évaluation de celui-ci n’est pas terminée). résoudre directement pb
→ appel de la méthode get() sur le Future } sinon { fork
décomposer le problème en parties indépendantes
class FonctionAsynchrone implements Callable<TypeRetour> {
fork : créer des (sous-)tâches
public TypeRetour call() { ... return v; }
pour résoudre chaque partie
}
join : attendre la réalisation de ces (sous-)tâches
fusionner les résultats partiels
ExecutorService executor = Executors.newCachedThreadPool();
retourner le Résultat
Callable<TypeRetour> fonc = new FonctionAsynchrone(); join
}
Future<TypeRetour> appel = executor.submit(fonc);
...
TypeRetour ret = appel.get(); // éventuellement bloquant
Systèmes concurrents – Java & Posix Threads 30 / 62 Systèmes concurrents – Java & Posix Threads 31 / 62

Généralités Moniteur Java Généralités Moniteur Java


Threads Java Autres objets de synchronisation Threads Java Autres objets de synchronisation
Synchronisation Java Régulation du parallélisme Synchronisation Java Régulation du parallélisme
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches Synchronisation (java d’origine)

Exécuteur pour le schéma fork/join (1/3) Exécuteur pour le schéma fork/join (2/3)
Activité d’un ouvrier du ForkJoinPool
Difficulté de la stratégie diviser pour régner :
schéma exponentiel + coût de la création d’activités Un ouvrier traite la tâche placée en tête de sa file
Un ouvrier appelant fork() ajoute les travaux créés en tête
Classe ForkJoinPool de sa propre file
Ensemble prédéterminé (pool) d’activités, →
chacune équipée d’une file d’attente de travaux à traiter. Chaque ouvrier traite un arbre de tâches qu’il
Les activités gérées sont des instances de ForkJoinTask
parcourt (et traite) en profondeur d’abord
(méthodes fork() et join())
(en préordre) → économie d’espace
P.invoke(j) invoke()
ForkJoinPool construit progressivement en largeur,
au fur et à mesure de son parcours :
traiter() + retour traiter() + retour traiter() + retour
lorsqu’un ouvrier descend d’un niveau, les
... ...
décomposer() + fork()
join() + fusionner() + retour
décomposer() + fork()
join() + fusionner() + retour
décomposer() + fork()
join() + fusionner() + retour
frères de la tâche à traiter sont créés, et
ouvrier ouvrier ouvrier placés en tête de la file d’attente
Systèmes concurrents – Java & Posix Threads 32 / 62 Systèmes concurrents – Java & Posix Threads 33 / 62
Généralités Moniteur Java Généralités Moniteur Java
Threads Java Autres objets de synchronisation Threads Java Autres objets de synchronisation
Synchronisation Java Régulation du parallélisme Synchronisation Java Régulation du parallélisme
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches Synchronisation (java d’origine)

Exécuteur pour le schéma fork/join (3/3) Synchronisation (Java ancien)

Vol de travail : lorsqu’une activité a épuisé les travaux de sa file,


elle prend un travail en queue d’une autre file Principe
exclusion mutuelle
La tâche prise correspond au dernier sous-
attente/signalement sur un objet
arbre (le plus proche de la racine) qui était
vol affecté à l’ouvrier  volé  équivalent à un moniteur avec une seule variable condition
2 0
→ pas de conflits si les sous-problèmes
0 1 sont bien partitionnés Obsolète : la protection par exclusion mutuelle (syncronized) sert
encore, mais éviter la synchronisation sur objet et préférer les
→ pas d’attente inutile pour l’ouvrier
 volé  puisque la tâche volée était
véritables moniteurs introduits dans Java 5.
file la dernière à traiter.

Systèmes concurrents – Java & Posix Threads 34 / 62 Systèmes concurrents – Java & Posix Threads 35 / 62

Généralités Moniteur Java Généralités Moniteur Java


Threads Java Autres objets de synchronisation Threads Java Autres objets de synchronisation
Synchronisation Java Régulation du parallélisme Synchronisation Java Régulation du parallélisme
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches Synchronisation (java d’origine)

Exclusion mutuelle Exclusion mutuelle

Tout objet Java est équipé d’un verrou d’exclusion mutuelle.


Chaque classe possède aussi un verrou exclusif qui s’applique aux
Code synchronisé
méthodes de classe (méthodes statiques) :
synchronized (unObj) {
< Excl. mutuelle vis-à-vis des autres class X {
blocs synchronized(unObj) > static synchronized T foo() { ... }
} static synchronized T’ bar() { ... }
}
Méthode synchronisée
synchronized assure l’exécution en exclusion mutuelle pour
synchronized T uneMethode(...) { ... } toutes les méthodes statiques synchronisées de la classe X.
(exclusion d’accès de l’objet sur lequel on applique la méthode, pas Ce verrou ne concerne pas l’exécution des méthodes d’objets.
de la méthode elle-même)

Systèmes concurrents – Java & Posix Threads 36 / 62 Systèmes concurrents – Java & Posix Threads 37 / 62
Généralités Moniteur Java
Threads Java Autres objets de synchronisation
Régulation du parallélisme
Synchronisation basique – exemple
Synchronisation Java
POSIX Threads & autres approches Synchronisation (java d’origine)
class StationVeloToulouse {
Synchronisation par objet private int nbVelos = 0;

public void prendre() throws InterruptedException {


Méthodes wait et notify[All] applicables à tout objet, pour synchronized(this) {
lequel l’activité a obtenu l’accès exclusif. while (this.nbVelos == 0) {
this.wait();
unObj.wait() libère l’accès exclusif à l’objet et bloque l’activité }
appelante en attente d’un réveil via une opération this.nbVelos--;
unObj.notify }
unObj.notify() réveille une unique activité bloquée sur l’objet, }
et la met en attente de l’obtention de l’accès exclusif
public void rendre() {
(si aucune activité n’est bloquée, l’appel ne fait rien) ;
// assume : toujours de la place
unObj.notifyAll() réveille toutes les activités bloquées sur synchronized(this) {
l’objet, qui se mettent toutes en attente de l’accès this.nbVelos++;
exclusif. this.notify();
}
}
Systèmes concurrents – Java & Posix Threads 38 / 62
}

Généralités Moniteur Java


Synchronisation basique – exemple Threads Java Autres objets de synchronisation
Régulation du parallélisme
Synchronisation Java
POSIX Threads & autres approches Synchronisation (java d’origine)
class BarriereBasique {
private final int N; Difficultés
private int nb = 0;
private boolean ouverte = false;
public BarriereBasique(int N) { this.N = N; } prises multiples de verrous :

public void franchir() throws InterruptedException { synchronized(o1) { synchronized(o2) { o1.wait(); } }


synchronized(this) {
this.nb++; une seule notification possible pour une exclusion mutuelle
this.ouverte = (this.nb >= N); donnée → résolution difficile de problèmes de synchronisation
while (! this.ouverte)
this.wait(); Pas des moniteurs de Hoare !
this.nb--; programmer comme avec des sémaphores
this.notifyAll();
} affecter un objet de blocage distinct à chaque requête et gérer
} soit-même les files d’attente
public synchronized void fermer() {
pas de priorité au signalé, pas d’ordonnancement sur les
if (this.nb == 0)
this.ouverte = false; déblocages
}
Systèmes concurrents – Java & Posix Threads 41 / 62
}
Généralités Moniteur Java Généralités Posix Threads
Threads Java Autres objets de synchronisation Threads Java Synchronisation Posix Thread
Synchronisation Java Régulation du parallélisme Synchronisation Java Autres approches
POSIX Threads & autres approches Synchronisation (java d’origine) POSIX Threads & autres approches

class Requ^ ete { Plan


bool ok;
// paramètres d’une demande 1 Généralités
}
List<Requ^ ete> file; 2 Threads Java
demande bloquante libération Manipulation des activités
req = new Requ^ ete(...)
synchronized(file) { synchronized(file) {
3 Synchronisation Java
if (satisfiable(req)) { // + maj état applicatif Moniteur Java
// + maj état applicatif ete r : file) {
for (Requ^ Autres objets de synchronisation
req.ok = true; synchronized(r) { Régulation du parallélisme
} else { if (satisfiable(r)) { Synchronisation (java d’origine)
file.add(req) // + maj état applicatif
} r.ok = true 4 POSIX Threads & autres approches
} r.notify(); Posix Threads
synchronized(req) { } Synchronisation Posix Thread
while (! req.ok) } Autres approches
req.wait(); }
}
Systèmes concurrents – Java & Posix Threads
} 42 / 62 Systèmes concurrents – Java & Posix Threads 43 / 62

Généralités Posix Threads Généralités Posix Threads


Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

Posix Threads Cycle de vie d’une activité

bloqué
Standard de librairie multi-activités pour le C, supporté par de Obtention, attente de ressources:
nombreuses implantations plus ou moins conformantes. Réveil
lock, wait, E/S, ...
Contenu de la bibliothèque :
manipulation d’activités (création, terminaison. . . ) création
Prêt préemption sur le processeur
synchronisation : verrous, variables condition.
primitives annexes : données spécifiques à chaque activité, Actif
politique d’ordonnancement. . . exit, fin du code
exit, fin du code
ajustement des primitives standard : processus lourd, E/S, (détaché) (non détaché)
signaux, routines réentrantes.
nettoyé terminé
join
(non détaché)
Systèmes concurrents – Java & Posix Threads 44 / 62 Systèmes concurrents – Java & Posix Threads 45 / 62
Généralités Posix Threads Généralités Posix Threads
Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

Création d’une activité Terminaison

int pthread_create (pthread_t *thread,


const pthread_attr_t *attr,
void * (*start_routine)(void *), void pthread_exit (void *status);
void *arg);
Termine l’activité appelante en fournissant un code de retour.
Crée une nouvelle activité pour exécuter la routine indiquée, pthread exit(NULL) est automatiquement exécuté en cas de
appelée avec l’argument arg. Les attributs sont utilisés pour terminaison du code de l’activité sans appel de pthread exit.
définir la priorité et la politique d’ordonnancement (scheduling
policy). thread contient l’identificateur de l’activité créée. int pthread_join (pthread_t thr, void **status);

pthread_t pthread_self (void); Attend la terminaison de l’activité et récupère le code retour.


int pthread_equal (pthread_t thr1, pthread_t thr2); L’activité ne doit pas être détachée ou avoir déjà été  jointe  .

self renvoie l’identificateur de l’activité appelante.


pthread equal : vrai si les arguments désignent la même activité.
Systèmes concurrents – Java & Posix Threads 46 / 62 Systèmes concurrents – Java & Posix Threads 47 / 62

Généralités Posix Threads Généralités Posix Threads


Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

Terminaison – 2 L’activité initiale


Au démarrage, une activité est automatiquement créée pour
exécuter la procédure main. Elle exécute une procédure de
démarrage qui contient le code :
int pthread_detach (pthread_t thread);
{ int r = main(argc,argv); exit(r); }
Détache l’activité thread.
Les ressources allouées pour l’exécution d’une activité (pile. . . ) ne Si la procédure main se termine, le process unix est ensuite terminé
sont libérées que lorsque l’activité s’est terminée et que : (par l’appel à exit), et non pas seulement l’activité initiale. Pour
éviter que la procédure main ne se termine alors qu’il reste des
ou join a été effectué,
activités :
ou l’activité a été détachée.
bloquer l’activité initiale sur l’attente de la terminaison d’une
ou plusieurs autres activités (pthread join) ;
terminer explicitement l’activité initiale avec pthread exit,
ce qui court-circuite l’appel de exit.
Systèmes concurrents – Java & Posix Threads 48 / 62 Systèmes concurrents – Java & Posix Threads 49 / 62
Généralités Posix Threads Généralités Posix Threads
Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

Données spécifiques Synchronisation PThread

Données spécifiques
Pour une clef donnée (partagée), chaque activité possède sa propre
Principe
valeur associée à cette clef.
Moniteur de Hoare élémentaire avec priorité au signaleur :
int pthread_key_create (pthread_key_t *clef, verrous
void (*destructeur)(void *)); variables condition
pas de transfert du verrou à l’activité signalée
int pthread_setspecific (pthread_key_t clef,
void *val);
void *pthread_getspecific (pthread_key_t clef);

Systèmes concurrents – Java & Posix Threads 50 / 62 Systèmes concurrents – Java & Posix Threads 51 / 62

Généralités Posix Threads Généralités Posix Threads


Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

Verrou Verrouillage/déverrouillage

int pthread_mutex_lock (pthread_mutex_t *m);


int pthread_mutex_trylock (pthread_mutex_t *m);
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; int pthread_mutex_unlock (pthread_mutex_t *m);

int pthread_mutex_init (pthread_mutex_t *mutex,


lock verrouille le verrou, avec blocage en attente si déjà
const pthread_mutex_attr *attr);
verrouillé. Renvoie 0 si ok.
int pthread_mutex_destroy (pthread_mutex_t *m); trylock verrouille le verrou si possible et renvoie 0, sinon
renvoie EBUSY si le verrou est déjà verrouillé.
unlock déverrouille. Seule l’activité qui a verrouillé m a le
droit de le déverrouiller.

Systèmes concurrents – Java & Posix Threads 52 / 62 Systèmes concurrents – Java & Posix Threads 53 / 62
Généralités Posix Threads Généralités Posix Threads
Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

Variable condition Attente/signal

int pthread_cond_wait (pthread_cond_t*,


pthread_mutex_t*);
int pthread_cond_timedwait (pthread_cond_t*,
pthread_cond_t vc = PTHREAD_COND_INITIALIZER; pthread_mutex_t*,
const struct timespec *abstime);
int pthread_cond_init (pthread_cond_t *vc,
const pthread_cond_attr *attr); cond wait l’activité appelante doit posséder le verrou spécifié.
L’activité se bloque sur la variable condition après
int pthread_cond_destroy (pthread_cond_t *vc); avoir libéré le verrou. L’activité reste bloquée jusqu’à
ce que vc soit signalée et que l’activité ait réacquis le
verrou.
cond timedwait comme cond wait avec délai de garde. À
l’expiration du délai de garde, le verrou est reobtenu
et la procédure renvoie ETIMEDOUT.
Systèmes concurrents – Java & Posix Threads 54 / 62 Systèmes concurrents – Java & Posix Threads 55 / 62

Généralités Posix Threads Généralités Posix Threads


Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

Attente/signal Ordonnancement

int pthread_cond_signal (pthread_cond_t *vc);


int pthread_cond_broadcast (pthread_cond_t *vc);
Par défaut : ordonnancement arbitraire pour l’acquisition d’un
cond signal signale la variable condition : une activité bloquée verrou ou le réveil sur une variable condition.
sur la variable condition est réveillée et tente de
réacquérir le verrou de son appel de cond wait. Elle Les activités peuvent avoir des priorités, et les verrous et variables
sera effectivement débloquée quand elle le réacquerra. conditions peuvent être créés avec respect des priorités.
cond broadcast toutes les activités en attente sont réveillées, et
tentent d’obtenir le verrou correspondant à leur appel
de cond wait.

Systèmes concurrents – Java & Posix Threads 56 / 62 Systèmes concurrents – Java & Posix Threads 57 / 62
Généralités Posix Threads Généralités Posix Threads
Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

Windows API (C, C++) .NET (C#)


Très similaire à Java :

Plus de 150 ( ?) fonctions, dont : Création d’activité :


t = new System.Threading.Thread(méthode);
création d’activité : CreateThread
Démarrage : t.Start();
exclusion mutuelle : InitializeCriticalSection,
Attente de terminaison : t.Join();
EnterCriticalSection, LeaveCriticalSection
Exclusion mutuelle : lock(objet) { ... }
synchronisation basique : WaitForSingleObject,
(mot clef du langage)
WaitForMultipleObjects, SetEvent
Synchronisation élémentaire :
synchronisation  évoluée  : SleepConditionVariableCS,
System.Threading.Monitor.Wait(objet);
WakeConditionVariable
System.Threading.Monitor.Pulse(objet); (= notify)
Note : l’API Posix Threads est aussi supportée (ouf). Sémaphore :
s = new System.Threading.Semaphore(nbinit,nbmax);
s.Release(); s.WaitOne();
Systèmes concurrents – Java & Posix Threads 58 / 62 Systèmes concurrents – Java & Posix Threads 59 / 62

Généralités Posix Threads Généralités Posix Threads


Threads Java Synchronisation Posix Thread Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches Synchronisation Java Autres approches
POSIX Threads & autres approches POSIX Threads & autres approches

OpenMP OpenMP avantages/inconvénients


API pour la programmation parallèle en C/C++/Fortran
+ simple
+ amélioration progressive du code
+ une seule version séquentielle / parallèle
+ peu de modifications sur le code séquentiel d’origine
− exclusivement multiprocesseurs à mémoire partagée
Annotations dans le code, interprétées par le compilateur − compilateur dédié
Boucle parallèle − peu de primitives de synchronisation (atomicité uniquement)
int i, a[N]; − gros travail sur du code mal conçu
#pragma omp parallel for − introduction de bugs en parallélisant du code non parallélisable
for (i = 0; i < N; i++)
a[i] = 2 * i;
Systèmes concurrents – Java & Posix Threads 60 / 62 Systèmes concurrents – Java & Posix Threads 61 / 62
Généralités Posix Threads
Threads Java Synchronisation Posix Thread
Synchronisation Java Autres approches
POSIX Threads & autres approches

Intel Threading Building Blocks

Bibliothèque pour C++


Structures de contrôles optimisées parallel for. . .
Structures de données optimisées concurrent queue. . .
Peu de primitives de synchronisation (exclusion mutuelle,
verrou lecteurs/rédacteurs)
Implantation spécialisée par modèle de processeur
Partage de tâches par  vol de travail 

Inconvénient : portabilité (compilateur + matériel)

Systèmes concurrents – Java & Posix Threads 62 / 62


Philosophes
Problèmes classiques Problèmes classiques Producteurs/consommateurs
Architectures Architectures Lecteurs/rédacteurs
Client/serveur Client/serveur Barrière

Plan

1 Problèmes classiques
Philosophes
Septième partie Producteurs/consommateurs
Lecteurs/rédacteurs
Barrière
Problèmes génériques 2 Architectures
Parallélisme
Pipeline
Ouvriers
3 Client/serveur
Une activité par client
Schéma général

Systèmes concurrents 2 / 15 Systèmes concurrents – problèmes génériques 3 / 15

Philosophes Philosophes
Problèmes classiques Producteurs/consommateurs Problèmes classiques Producteurs/consommateurs
Architectures Lecteurs/rédacteurs Architectures Lecteurs/rédacteurs
Client/serveur Barrière Client/serveur Barrière

Philosophes et spaghettis – Dijkstra Producteurs/consommateurs

N philosophes sont autour d’une table. Consommateur


Il y a une assiette par philosophe, et Producteur
une fourchette entre chaque assiette.
Pour manger, un philosophe doit Producteur tampon Consommateur
utiliser les deux fourchettes adjacentes borné
à son assiette (et celles-là seulement). Consommateur
Un philosophe peut être : tampon de taille borné et fixé
penseur : il n’utilise pas de fourchettes ; nombre indéterminé et dynamique de producteurs
mangeur : il utilise les deux fourchettes adjacentes ; aucun de ” ” ” ” de consommateurs
ses voisins ne peut manger ;
demandeur : il souhaite manger mais ne dispose pas des deux objectifs : ne pas écraser une case occupée, une unique lecture
fourchettes. consommatrice par case, attendre pour déposer si plein,
attendre pour retirer si vide
Ce problème est analogue au problème de l’allocateur multi-classes
multi-ressources.
Systèmes concurrents – problèmes génériques 4 / 15 Systèmes concurrents – problèmes génériques 5 / 15
Philosophes Philosophes
Problèmes classiques Producteurs/consommateurs Problèmes classiques Producteurs/consommateurs
Architectures Lecteurs/rédacteurs Architectures Lecteurs/rédacteurs
Client/serveur Barrière Client/serveur Barrière

Lecteurs/rédacteurs Barrière

1 Barrière élémentaire : ensemble d’activités qui attendent


Une ressource peut être utilisée :
mutuellement qu’elles soient toutes au même point
concurremment par plusieurs lecteurs (plusieurs lecteurs (rendez-vous multiple)
simultanément) ; 2 Barrière généralisée :
exclusivement par un rédacteur (pas d’autre rédacteur, pas barrière de taille M alors qu’il existe N candidats (N > M)
d’autre lecteur). barrière réutilisable (cyclique) : nécessité de la refermer
Souvent rencontré sous la forme de verrou lecture/écriture
(read-write lock). Schéma de code join
séquentiel fork
Permet l’isolation des modifications avec un meilleur parallélisme parallélisme
que l’exclusion mutuelle.  fork-join 
code
parallèle

Systèmes concurrents – problèmes génériques 6 / 15 Systèmes concurrents – problèmes génériques 7 / 15

Problèmes classiques Parallélisme Problèmes classiques Parallélisme


Architectures Pipeline Architectures Pipeline
Client/serveur Ouvriers Client/serveur Ouvriers

Plan Bénéficier du parallélisme

1 Problèmes classiques
Philosophes
Producteurs/consommateurs
Lecteurs/rédacteurs
Barrière travail délégué (faible priorité)
travail anticipé (prévision des requêtes futures)
2 Architectures
actions événementielles ou périodiques (alarmes,
Parallélisme
surveillants. . . )
Pipeline
Ouvriers
3 Client/serveur
Une activité par client
Schéma général

Systèmes concurrents – problèmes génériques 8 / 15 Systèmes concurrents – problèmes génériques 9 / 15


Problèmes classiques Parallélisme Problèmes classiques Parallélisme
Architectures Pipeline Architectures Pipeline
Client/serveur Ouvriers Client/serveur Ouvriers

Pipeline : chaı̂ne d’activités Ouvriers autonomes

Ouvrier
entrée filtre A filtre B filtre C sortie

succession de filtres
travaux Ouvrier résultats
= suite de producteur/consommateur 1 − 1

Ouvrier

Systèmes concurrents – problèmes génériques 10 / 15 Systèmes concurrents – problèmes génériques 11 / 15

Problèmes classiques Parallélisme Problèmes classiques Une activité par client


Architectures Pipeline Architectures Schéma général
Client/serveur Ouvriers Client/serveur

Ouvriers + manager Plan

1 Problèmes classiques
Philosophes
Producteurs/consommateurs
Ouvrier
Lecteurs/rédacteurs
Barrière
travaux Manager Ouvrier Livreur résultats 2 Architectures
Parallélisme
Ouvrier
Pipeline
Ouvriers
3 Client/serveur
Une activité par client
Schéma général

Systèmes concurrents – problèmes génériques 12 / 15 Systèmes concurrents – problèmes génériques 13 / 15


Problèmes classiques Une activité par client Problèmes classiques Une activité par client
Architectures Schéma général Architectures Schéma général
Client/serveur Client/serveur

Client/serveur Client/serveur général


Superviseur

Ouvrier
Répondeur
Schéma élémentaire : Portier
Ouvrier
un ouvrier par requête : lit la requête, la traite, y répond Répondeur
création dynamique d’ouvrier Portier Ouvrier

adapté à des petits serveurs Ouvrier


Répondeur

ne supporte pas la montée en charge


activités de récupération et mise en forme des requêtes
activités de traitement
activités de réponse
nombre statique ou dynamique-borné
Systèmes concurrents – problèmes génériques 14 / 15 Systèmes concurrents – problèmes génériques 15 / 15
Processus communicants Processus communicants
Communication synchrone – CSP/CCS/Go Communication synchrone – CSP/CCS/Go
Rendez-vous étendu – Ada Rendez-vous étendu – Ada

Contenu de cette partie

Modèles de programmation concurrente


Septième partie Modèle des processus communicants
Approche CSP/Go pour la programmation concurrente
Goroutine et canaux
Communiquer explicitement plutôt que partager implicitement
Processus communicants Approche Ada pour la programmation concurrente
Tâches et rendez vous
Démarche de conception d’applications concurrentes en Ada
Transposition de la démarche vue dans le cadre de la mémoire
partagée (moniteurs)
Extension tirant parti des possibilités de contrôle fin offertes
par Ada

Systèmes concurrents 2 / 55 Systèmes concurrents 3 / 55

Processus communicants Processus communicants


Communication synchrone – CSP/CCS/Go Communication synchrone – CSP/CCS/Go
Rendez-vous étendu – Ada Rendez-vous étendu – Ada

Modèles d’interaction : mémoire partagée Modèles d’interaction : processus communicants


Donnée Processus
Donnée Processus
    boucle
      attendre()     
      traiter

oir
éme
ttre/r

v
      signaler()

ce
re ecev
cri oir

/re
/é     fin_boucle
lire

re
ett
ém
    
3
X
rire lire cevoir
    boucle
lire/é
c /éc      émettre/recevoir émettre/re
      attendre() toto rire     
    boucle
      traiter lire/écrire
      attendre()
      signaler() lire/écrire       traiter
    fin_boucle       signaler()
    fin_boucle Données encapsulées par les processus
Mémoire partagée (RAM)
Communication nécessaire, explicite : échange de messages
Communication implicite Programmation et interactions plus lourdes
résulte de l’accès et de la manipulation des variables partagées Visibilité des interactions → possibilité de trace/supervision
l’identité des processus n’intervient pas dans l’interaction Isolation des données
Synchronisation explicite (et nécessaire) Synchronisation implicite : attente de message
Architectures/modèles cibles Architectures/modèles cibles
multiprocesseurs à mémoire partagée, systèmes répartis : sites distants, reliés par un réseau
programmes multiactivités moniteurs, CSP/Erlang/Go, tâches Ada
Systèmes concurrents 4 / 55 Systèmes concurrents 5 / 55
Processus communicants Principes Processus communicants Principes
Communication synchrone – CSP/CCS/Go Désignation, alternatives Communication synchrone – CSP/CCS/Go Désignation, alternatives
Rendez-vous étendu – Ada Architecture d’une application parallèle Rendez-vous étendu – Ada Architecture d’une application parallèle

Plan Processus communicants

1 Processus communicants
Principes Synchronisation obtenue via des primitives de communication
Désignation, alternatives bloquantes : envoi (bloquant) de messages / réception bloquante
Architecture d’une application parallèle de messages

2 Communication synchrone – CSP/CCS/Go Communicating Sequential Processes (CSP) / Calculus of


Principes Communicating Systems (CCS) / π-calcul / Erlang / Go
Recherche concurrente Ada
Lecteurs/rédacteurs
Les principes détaillés des échanges et leur utilisation pour
3 Rendez-vous étendu – Ada
développer des applications sont vus dans le module
Principe du rendez-vous  intergiciels . On ne s’intéresse ici qu’à la synchronisation.
Mise en œuvre en Ada
Méthodologie par machine à états

Systèmes concurrents 6 / 55 Systèmes concurrents 7 / 55

Processus communicants Principes Processus communicants Principes


Communication synchrone – CSP/CCS/Go Désignation, alternatives Communication synchrone – CSP/CCS/Go Désignation, alternatives
Rendez-vous étendu – Ada Architecture d’une application parallèle Rendez-vous étendu – Ada Architecture d’une application parallèle

Quelle synchronisation ? Désignation du destinataire et de l’émetteur


Réception
Réception bloquante : attendre un message
Nommage
Émission Direct : désignation de l’activité émettrice/destinataire
Émission non bloquante ou asynchrone SEND message TO processName
Émission bloquante ou synchrone : bloque jusqu’à la réception RECV message FROM processName
du message = rendez-vous élémentaire entre l’activité Indirect : désignation d’une boı̂te à lettres ou d’un canal de
émettrice et l’activité destinataire communication
SEND message TO channel
Rendez-vous étendu : bloquant jusqu’à réception + réaction
RECV message FROM channel
+ réponse ≈ appel de procédure

Émission asynchrone ⇒ buffers (messages émis non reçus)


Synchrone ⇒ 1 case maximum
Systèmes concurrents 8 / 55 Systèmes concurrents 9 / 55
Processus communicants Principes Processus communicants Principes
Communication synchrone – CSP/CCS/Go Désignation, alternatives Communication synchrone – CSP/CCS/Go Désignation, alternatives
Rendez-vous étendu – Ada Architecture d’une application parallèle Rendez-vous étendu – Ada Architecture d’une application parallèle

Multiplicité Alternative

1−1
Désignation de l’activité : 1 émetteur / 1 récepteur désignés Alternative en émission ou en réception = choix parmi un ensemble
de communications possibles :
n−1 RECV msg FROM channel1 OR channel2
Canal réservé en lecture (consommation) : envoi par n’importe (SEND msg1 TO pid1) OR (SEND msg2 TO pid2)
quelle activité ; réception par une seule, propriétaire du canal (RECV msg1 FROM channel1) OR (SEND msg2 TO channel2)

n−m Si aucun choix n’est faisable ⇒ attendre


Canal avec envoi par n’importe qui, réception par n’importe qui : Si un seul des choix est faisable ⇒ le faire
pas de duplication : un seul destinataire consomme le message Si plusieurs choix sont faisables ⇒ sélection non-déterministe
duplication à tous les destinataires (diffusion) (arbitraire)
En mode synchrone, la diffusion est complexe et coûteuse à mettre en
œuvre (nécessite une synchronisation globale entre tous les récepteurs)
Systèmes concurrents 10 / 55 Systèmes concurrents 11 / 55

Processus communicants Principes Processus communicants Principes


Communication synchrone – CSP/CCS/Go Désignation, alternatives Communication synchrone – CSP/CCS/Go Désignation, alternatives
Rendez-vous étendu – Ada Architecture d’une application parallèle Rendez-vous étendu – Ada Architecture d’une application parallèle

Divers Architecture

La résolution des problèmes de synchronisation classiques


(producteurs/consommateurs. . . ) ne se fait plus en synchronisant
Asynchrone ⇒ perte de messages ? (buffer plein par exemple) directement les activités via des données partagées, mais
Construction d’une émission bloquante (rendez-vous) en cas indirectement via une activité de synchronisation.
d’émission non bloquante :
(SENT m TO ch; RECV FROM ack) demander
l'accès demander
k (RECV m FROM ch; SENT TO ack) en lecture l'accès
lecteur en écriture
Construction d’une émission non bloquante à partir d’émission rédacteur
bloquante : introduire une boı̂te intermédiaire qui accepte gestionnaire
lecteur LR
immédiatement tout message et le stocke dans une file.
rédacteur
lecteur
rendre l'accès
en lecture

Systèmes concurrents 12 / 55 Systèmes concurrents 13 / 55


Processus communicants Principes Processus communicants Principes
Communication synchrone – CSP/CCS/Go Désignation, alternatives Communication synchrone – CSP/CCS/Go Désignation, alternatives
Rendez-vous étendu – Ada Architecture d’une application parallèle Rendez-vous étendu – Ada Architecture d’une application parallèle

Activité arbitre pour un objet partagé Intérêt


Interactions avec l’objet partagé
Pour chaque opération Op, + découplage entre les activités clientes : l’interface partagée est
émettre un message de requête vers l’arbitre celle de l’activité de synchronisation
attendre le message de réponse de l’arbitre + réalisation centralisée et répartie
(⇒ se synchroniser avec l’arbitre) + transfert explicite d’information : traçage
+ pas de données partagées ⇒ pas de protection nécessaire
Schéma de fonctionnement de l’arbitre
+ contrôle fin des interactions
L’arbitre exécute une boucle infinie contenant une alternative
+ schéma naturel côté client : question/réponse = appel de
Cette alternative possède une branche par opération fournie fonction
Chaque branche est gardée par la condition d’acceptation de − multiples recopies (mais optimisations possibles)
l’opération (suivie de l’attente du rendez-vous correspondant)
− parallélisation du service : au cas par cas
Note : en communication synchrone, on peut parfois se passer du
message de réponse.
Systèmes concurrents 14 / 55 Systèmes concurrents 15 / 55

Processus communicants Principes Processus communicants Principes


Communication synchrone – CSP/CCS/Go Recherche concurrente Communication synchrone – CSP/CCS/Go Recherche concurrente
Rendez-vous étendu – Ada Lecteurs/rédacteurs Rendez-vous étendu – Ada Lecteurs/rédacteurs

Plan Go language
Principes de conception
1 Processus communicants
Principes Syntaxe légère inspirée du C
Désignation, alternatives Typage statique fort avec inférence
Architecture d’une application parallèle Interfaces avec extension et polymorphisme (typage
structurel / duck typing à la Smalltalk)
2 Communication synchrone – CSP/CCS/Go
Principes Ramasse-miettes
Recherche concurrente
Concepts pour la concurrence
Lecteurs/rédacteurs
Descendant de CSP (Hoare 1978), cousin d’Erlang
3 Rendez-vous étendu – Ada Goroutine ∼ activité/thread
Principe du rendez-vous une fonction s’exécutant indépendant (avec sa pile)
Mise en œuvre en Ada très léger (plusieurs milliers sans problème)
Méthodologie par machine à états géré par le noyau Go qui alloue les ressources processeurs
Canaux pour la communication et la synchronisation
Systèmes concurrents 16 / 55 Systèmes concurrents 17 / 55
Processus communicants Principes Processus communicants Principes
Communication synchrone – CSP/CCS/Go Recherche concurrente Communication synchrone – CSP/CCS/Go Recherche concurrente
Rendez-vous étendu – Ada Lecteurs/rédacteurs Rendez-vous étendu – Ada Lecteurs/rédacteurs

Go – canaux Go – canaux

Canaux
Canaux Alternative en réception et émission :
Création : make(chan type) ou make(chan type, 10) select {
(synchrone / asynchrone avec capacité) case v1 := <- chan1:
fmt.Printf("received %v from chan1\n", v1)
Envoi d’une valeur sur le canal chan : chan <- valeur
case v2 := <- chan2:
Réception d’une valeur depuis chan : <- chan fmt.Printf("received %v from chan2\n", v2)
Canal transmissible en paramètre ou dans un canal : case chan3 <- 42:
chan chan int est un canal qui transporte des canaux fmt.Printf("sent %v to chan3\n", 42)
(transportant des entiers) default:
fmt.Printf("no one ready to communicate\n")
}

Systèmes concurrents 18 / 55 Systèmes concurrents 19 / 55

Processus communicants Principes Processus communicants Principes


Communication synchrone – CSP/CCS/Go Recherche concurrente Communication synchrone – CSP/CCS/Go Recherche concurrente
Rendez-vous étendu – Ada Lecteurs/rédacteurs Rendez-vous étendu – Ada Lecteurs/rédacteurs

Exemple élémentaire Moteur de recherche

func boring(msg string, c chan string) { Objectif : agrégation de la recherche dans plusieurs bases
for i := 0; ; i++ {
func Web(query string) Result
c <- fmt.Sprintf("%s %d", msg, i)
func Image(query string) Result
time.Sleep(time.Duration(rand.Intn(4)) * time.Second)
func Video(query string) Result
}
}
Moteur séquentiel
func main() { func Google(query string) (results []Result) {
c := make(chan string) results = append(results, Web(query))
go boring("boring!", c) results = append(results, Image(query))
for i := 0; i < 5; i++ { results = append(results, Video(query))
fmt.Printf("You say: %q\n", <- c) return
} }
fmt.Println("You’re boring; I’m leaving.")
} exemple tiré de https://talks.golang.org/2012/concurrency.slide

Systèmes concurrents 20 / 55 Systèmes concurrents 21 / 55


Processus communicants Principes Processus communicants Principes
Communication synchrone – CSP/CCS/Go Recherche concurrente Communication synchrone – CSP/CCS/Go Recherche concurrente
Rendez-vous étendu – Ada Lecteurs/rédacteurs Rendez-vous étendu – Ada Lecteurs/rédacteurs

Recherche concurrente Le temps sans interruption

Moteur concurrent Crée un canal sur lequel un message sera envoyé après la durée
func Google(query string) (results []Result) { spécifiée.
c := make(chan Result) time.After
go func() { c <- Web(query) } ()
go func() { c <- Image(query) } () func After(d time.Duration) <-chan bool {
go func() { c <- Video(query) } () // returns a receive-only channel
c := make(chan bool)
for i := 0; i < 3; i++ { go func() {
result := <-c time.Sleep(d)
results = append(results, result) c <- true
} }()
return return c
} }

Systèmes concurrents 22 / 55 Systèmes concurrents 23 / 55

Processus communicants Principes Processus communicants Principes


Communication synchrone – CSP/CCS/Go Recherche concurrente Communication synchrone – CSP/CCS/Go Recherche concurrente
Rendez-vous étendu – Ada Lecteurs/rédacteurs Rendez-vous étendu – Ada Lecteurs/rédacteurs

Recherche concurrente rapide Recherche répliquée


Moteur concurrent avec timeout
c := make(chan Result) Utiliser plusieurs serveurs répliqués et garder la réponse du premier
go func() { c <- Web(query) } ()
qui répond.
go func() { c <- Image(query) } ()
go func() { c <- Video(query) } () Recherche en parallèle
func First(query string, replicas ...Search) Result {
timeout := time.After(80 * time.Millisecond)
c := make(chan Result)
for i := 0; i < 3; i++ {
searchReplica := func(i int) { c <- replicas[i](query) }
select {
for i := range replicas
case result := <-c:
go searchReplica(i)
results = append(results, result)
}
case <-timeout:
return <-c
fmt.Println("timed out")
return
}
}
return
Systèmes concurrents 24 / 55 Systèmes concurrents 25 / 55
Processus communicants Principes Processus communicants Principes
Communication synchrone – CSP/CCS/Go Recherche concurrente Communication synchrone – CSP/CCS/Go Recherche concurrente
Rendez-vous étendu – Ada Lecteurs/rédacteurs Rendez-vous étendu – Ada Lecteurs/rédacteurs

Recherche répliquée Bilan


Moteur concurrent répliqué avec timeout
c := make(chan Result) Création ultra-légère de goroutine : penser concurrent
go func() { c <- First(query, Web1, Web2, Web3) } () Pas besoin de variables partagées
go func() { c <- First(query, Image1, Image2) } ()
go func() { c <- First(query, Video1, Video2) } ()
⇒ Pas de verrous
timeout := time.After(80 * time.Millisecond) Pas besoin de variable condition pour synchroniser
for i := 0; i < 3; i++ { Pas besoin de callback ou d’interruption
select {
case result := <-c:
results = append(results, result) Don’t communicate by sharing memory, share memory by
case <-timeout: communicating.
fmt.Println("timed out")
return
} (la bibliothèque Go contient aussi les objets usuels de synchronisation
} pour travailler en mémoire partagée : verrous, sémaphores, moniteur. . . )
return
Systèmes concurrents 26 / 55 Systèmes concurrents 27 / 55

Processus communicants Principes Processus communicants Principes


Communication synchrone – CSP/CCS/Go Recherche concurrente Communication synchrone – CSP/CCS/Go Recherche concurrente
Rendez-vous étendu – Ada Lecteurs/rédacteurs Rendez-vous étendu – Ada Lecteurs/rédacteurs

Lecteurs/rédacteurs Lecteurs/rédacteurs

demander
l'accès demander Utilisateur
en lecture l'accès
lecteur en écriture func Utilisateur() {
rédacteur nothing := struct{}{}
for {
gestionnaire
lecteur LR DL <- nothing; // demander lecture
...
rédacteur TL <- nothing; // terminer lecture
lecteur ...
rendre l'accès
en lecture DE <- nothing; // demander écriture
...
Un canal pour chaque type de requête TE <- nothing; // terminer écriture
Émission bloquante ⇒ accepter un message (une requête) }
uniquement si l’état l’autorise }

Systèmes concurrents 28 / 55 Systèmes concurrents 29 / 55


Processus communicants Principe du rendez-vous
Lecteurs/rédacteurs Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Méthodologie par machine à états
Rendez-vous étendu – Ada
Goroutine de synchronisation
Plan
func when(b bool, c chan struct{}) chan struct{} {
if b { return c } else { return nil }
} 1 Processus communicants
Principes
func SynchroLR() { Désignation, alternatives
nblec := 0;
Architecture d’une application parallèle
ecr := false;
for { 2 Communication synchrone – CSP/CCS/Go
select { Principes
case <- when(nblec == 0 && !ecr, DE):
Recherche concurrente
ecr := true;
case <- when(!ecr, DL): Lecteurs/rédacteurs
nblec++; 3 Rendez-vous étendu – Ada
case <- TE:
ecr := false;
Principe du rendez-vous
case <- TL: Mise en œuvre en Ada
nblec--; Méthodologie par machine à états
}
}}
Systèmes concurrents 31 / 55

Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous


Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Modèle Ada Principe du rendez-vous

Une tâche possède des points d’entrée de rendez-vous.


Intérêt Une tâche peut :
demander un rendez-vous avec une autre tâche désignée
Modèle adapté à la répartition, contrairement aux sémaphores
explicitement ;
ou aux moniteurs, intrinsèquement centralisés. attendre un rendez-vous sur un (ou plusieurs) point(s) d’entrée.
Similaire au modèle client-serveur. Un rendez-vous est dissymétrique : tâche appelante ou cliente
Contrôle plus fin du moment où les interactions ont lieu. vs tâche appelée ou serveur.
Vocabulaire : tâche = activité Échanges de données :
lors du début du rdv, de l’appelant vers l’appelé ;
lors de la fin du rdv, de l’appelé vers l’appelant.

Systèmes concurrents 32 / 55 Systèmes concurrents 33 / 55


Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous
Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Rendez-vous – client en premier Rendez-vous – serveur en premier

T1 appelante T2 appelée
T1 appelante T2 appelée

acceptation du rdv
demande de rdv
bloquée
demande de rdv
b param d'entrée acceptation du rdv
l param d'entrée
o b
q exécution l exécution
u du corps o du corps
é du rdv q du rdv
e u
é
param de retour fin du rdv e
param de retour fin du rdv

Systèmes concurrents 34 / 55 Systèmes concurrents 35 / 55

Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous


Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Principe du rendez-vous Déclaration d’une tâche

Déclaration
Si un client demande un rendez-vous alors que le serveur n’est
task <nom> is
pas prêt à l’accepter, le client se bloque en attente de
{ entry <point d’entrée> (<param formels>); }+
l’acceptation.
end
Si un serveur indique qu’il est prêt à accepter un rendez-vous
et qu’il n’y a pas de demandeur, il se bloque. Exemple
En outre, l’appelant est bloqué pendant l’exécution du corps task X is
du rendez-vous. entry A;
entry B (msg : in T);
Important : il est impossible d’accepter/refuser un rendez-vous entry C (x : out T);
selon la valeur des paramètres. entry D (a : in T1; b : out T2);
end X

Systèmes concurrents 36 / 55 Systèmes concurrents 37 / 55


Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous
Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Appel de rendez-vous Acceptation d’un rendez-vous


Acceptation
accept <point d’entrée> (<param formels>)
[ do
Appel de rdv
{ <instructions> }+
<nom t^ache>.<point d’entrée> (<param effectifs>); end <point d’entrée> ]
Similaire à un appel de procédure.
Exemple
Exemple accept D (a : in Natural; b : out Natural) do
X.A; if a > 6 then
X.D(x,y); b := a / 4;
else
b := a + 2;
end if;
end D;
Systèmes concurrents 38 / 55 Systèmes concurrents 39 / 55

Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous


Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Acceptation parmi un ensemble Producteurs/consommateurs

Alternative gardée Déclaration du serveur


select task ProdCons is
when C1 => entry Deposer (msg: in T);
accept E1 do entry Retirer (msg: out T);
... end ProdCons;
end E1;
or Client : utilisation
when C2 => begin
accept E2 do -- engendrer le message m1
... ProdCons.Deposer (m1);
end E2; -- ...
or ProdCons.Retirer (m2);
... -- utiliser m2
end select; end
Systèmes concurrents 40 / 55 Systèmes concurrents 41 / 55
Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous
Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

task body ProdCons is Producteurs/consommateurs – un exemple d’exécution


Libre : integer := N;
begin
task body Prod is task body ProdCons is task body Conso is
loop … … …
select begin begin begin
when Libre > 0 => … … …
… accept Deposer (msg : in T) do …
accept Deposer (msg : in T) do … ProdCons.Retirer (m);
deposer_dans_tampon(msg); m := … (serveur attend)

e
ProdCons.Deposer (m);

tré
end Deposer; para entrée

'en
(client attend) deposer_dans_tampon(msg);

sd
Libre := Libre - 1;

re
… para sortie end Deposer;

èt
m
… nbOcc := nbOcc + 1; (client attend)
or

ra
pa
end Prod; …
when Libre < N => …
accept Retirer (msg : out T) do accept Retirer (msg : out T) do
msg := retirer_du_tampon(); retirer_du_tampon(msg);
end Retirer; paramètres de sortie
end Retirer; nbOcc := nbOcc - 1; utiliser(m);
Libre := Libre + 1; … …
… …
end select; end ProdCons; end Conso;
end loop;
end ProdCons;
Systèmes concurrents 42 / 55 Systèmes concurrents 43 / 55

Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous


Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Remarques Allocateur de ressources

Un système comporte des ressources critiques c’est-à-dire non


Les accept ne peuvent figurer que dans le corps des tâches. partageables et non préemptibles, comme les pages mémoire.
accept sans corps → synchronisation pure. L’allocateur de ressources est un service qui permet à un processus
Une file d’attente (FIFO) est associée à chaque entrée. d’acquérir par une seule action plusieurs ressources. On ne
s’intéresse qu’à la synchronisation et on ne s’occupe pas de la
rdv’count (attribut des entrées) donne le nombre de clients
gestion effective des identifiants de ressources.
en attente sur une entrée donnée.
La gestion et la prise en compte des appels diffèrent par Déclaration du serveur
rapport aux moniteurs : task Allocateur is
la prise en compte d’un appel au service est déterminée par le entry demander (nbDemandé: in natural;
serveur ; id : out array of RessourceId);
plusieurs appels à un même service peuvent déclencher des entry rendre (nbRendu: in natural;
traitements différents ;
id : in array of RessourceId);
le serveur peut être bloqué, tandis que des clients attendent.
end Allocateur;

Systèmes concurrents 44 / 55 Systèmes concurrents 45 / 55


Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous
Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

task body Allocateur is Méthodologie par machine à états


nbDispo : integer := N;
begin
loop Construire un automate fini à états :
select identifier les états du système
accept Demander (nbDemandé : in natural) do
while nbDemandé > nbDispo loop un état est caractérisé par les rendez-vous acceptables
accept Rendre (nbRendu : in natural) do un rendez-vous accepté change (éventuellement) l’état
nbDispo := nbDispo + nbRendu;
end Rendre;
end loop; Producteurs/consommateurs à 2 cases
nbDispo := nbDispo - nbDemandé;
Déposer Déposer
end Demander;
or
accept Rendre (nbRendu : in natural) do Vide NiVideNiPlein Plein
nbDispo := nbDispo + nbRendu;
end Rendre; Retirer Retirer
end select;
end loop;
end
Systèmes Allocateur;
concurrents 46 / 55 Systèmes concurrents 47 / 55

Processus communicants Principe du rendez-vous


Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
task body ProdCons is Rendez-vous étendu – Ada Méthodologie par machine à états
type EtatT is (Vide, NiVideNiPlein, Plein);
etat : EtatT := Vide;
begin
loop
if etat = Vide then
select elsif etat = Plein then
accept Deposer (msg : in T) do select
deposer_dans_tampon(msg);
accept Retirer (msg : out T) do
end Deposer;
etat := NiVideNiPlein; msg := retirer_du_tampon();
end select; end Retirer;
elsif etat = NiVideNiPlein then etat := NiVideNiPlein;
select end select;
accept Deposer (msg : in T) do end if;
deposer_dans_tampon(msg); end loop;
end Deposer; end ProdCons;
etat := Plein;
or
accept Retirer (msg : out T) do
msg := retirer_du_tampon();
end Retirer;
etat := Vide;
end select; Systèmes concurrents 49 / 55
Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous
Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Automate paramétré Lecteurs/rédacteurs

Représenter un ensemble d’états comme un unique état paramétré. Lecteurs/rédacteurs


Les valeurs du paramètre différenciant les états de l’ensemble
peuvent être utilisées pour étiqueter les transitions. Libre
DL DE
Producteurs/consommateurs à N cases
Déposer
NiVideNiPlein avec NbOcc = N−1 TL
Déposer TE
1 lect 1 réd
DL
Vide UneCase N−1 Plein

Retirer Retirer DL = demander lecture


avec NbOcc = 1
TL DE = demander écriture
2 lect TL = terminer lecture
TE = terminer écriture
Retirer avec NbOcc /= 1 Déposer avec NbOcc /= N−1

Systèmes concurrents 50 / 55 Systèmes concurrents 51 / 55

Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous


Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Lecteurs/rédacteurs prio rédacteurs Dynamicité : activation de tâche

Lecteurs/rédacteurs prio rédacteurs


Une tâche peut être activée :
DL si statiquement : chaque task T, déclarée explicitement, est
DE’count = 0
Libre activée au démarrage du programme, avant l’initialisation des
DE
modules qui utilisent T.entry .
dynamiquement :
déclaration par task type T
TL TE activation par allocation : var t is access T := new T;
lect(s) 1 réd possibilité d’activer plusieurs tâches d’interface T.
DL si TL
DE’count = 0

Systèmes concurrents 52 / 55 Systèmes concurrents 53 / 55


Processus communicants Principe du rendez-vous Processus communicants Principe du rendez-vous
Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada Communication synchrone – CSP/CCS/Go Mise en œuvre en Ada
Rendez-vous étendu – Ada Méthodologie par machine à états Rendez-vous étendu – Ada Méthodologie par machine à états

Dynamicité :Terminaison Bilan processus communicants

Une tâche T est potentiellement appelante de T 0 si


T 0 est une tâche statique et le code de T contient au moins
+ Pas de partage implicite de la mémoire (→ isolation)
une référence à T 0 ,
+ Transfert explicite d’information (→ traçage)
ou T 0 est une tâche dynamique et (au moins) une variable du
code de T référence T 0 . + Réalisation centralisée et répartie
Une tâche se termine quand : + Contrôle fin des interactions
elle atteint la fin de son code, ∼ Méthodologie
ou elle est bloquée en attente de rendez-vous sur un select − Performance (copies)
avec clause terminate et toutes les tâches potentiellement − Quelques schémas classiques, faire preuve d’invention
appelantes sont terminées. (→ attention aux doigts)

La terminaison est difficile !

Systèmes concurrents 54 / 55 Systèmes concurrents 55 / 55


Transaction Transaction
Atomicité/Tout ou rien Atomicité/Tout ou rien
Contrôle de concurrence Contrôle de concurrence
Mémoire transactionnelle Mémoire transactionnelle

Contenu de cette partie

Nouvelle approche : programmation concurrente déclarative


Huitième partie Mise en œuvre de cette approche déclarative : notion de
transaction (issue du domaine des SGBD)
Protocoles réalisant les propriétés de base d’un service
Transactions transactionnel
Atomicité (possibilité d’annuler les effets d’un traitement)
Isolation (non interférence entre traitements)
Adaptation de la notion de transaction au modèle
de la programmation concurrente avec mémoire partagée
(mémoire transactionnelle)

Systèmes concurrents 2 / 59 Systèmes concurrents – Transactions 3 / 59

Transaction Interférences entre actions Transaction Interférences entre actions


Atomicité/Tout ou rien Définition des transactions Atomicité/Tout ou rien Définition des transactions
Contrôle de concurrence Type d’incohérences Contrôle de concurrence Type d’incohérences
Mémoire transactionnelle Mémoire transactionnelle

Plan Interférences et isolation


1 Transaction
Interférences entre actions
Définition des transactions
Type d’incohérences Objets partagés + actions concurrentes ⇒ résultats cohérents ?
Approches :
2 Atomicité/Tout ou rien
directe : synchronisation des actions contrôlant explicitement
3 Contrôle de concurrence l’attente/la progression des processus (p.e. exclusion mutuelle)
Principe
indirecte : contrôle de concurrence
Modélisation
assurer un contrôle transparent assurant l’équivalence à un
Méthodes
résultat cohérent
4 Mémoire transactionnelle
Intégration dans un langage
Difficultés
Implantation : STM, HTM

Systèmes concurrents – Transactions 4 / 59 Systèmes concurrents – Transactions 5 / 59


Transaction Interférences entre actions Transaction Interférences entre actions
Atomicité/Tout ou rien Définition des transactions Atomicité/Tout ou rien Définition des transactions
Contrôle de concurrence Type d’incohérences Contrôle de concurrence Type d’incohérences
Mémoire transactionnelle Mémoire transactionnelle

Contraintes d’intégrité Transaction


Définition
Suite d’opérations menant à un état cohérent, à partir de tout état
États cohérents décrits en intention par des contraintes d’intégrité cohérent.
(prédicats portant sur les valeurs des données).
masquer les états intermédiaires
Exemple  parenthésage  des états non observables
Base de données bancaire possibilité d’abandon sans effet visible
données = ensemble des comptes ⇒ transaction validée (committed).
contraintes : t3
t4
la somme des comptes est constante exécution t1
chaque compte est positif parallèle t2

temps
une exécution physique
séquentielle t1 t2 t3 t4
Systèmes concurrents – Transactions 6 / 59 Systèmes concurrents – Transactions 7 / 59

Transaction Interférences entre actions Transaction Interférences entre actions


Atomicité/Tout ou rien Définition des transactions Atomicité/Tout ou rien Définition des transactions
Contrôle de concurrence Type d’incohérences Contrôle de concurrence Type d’incohérences
Mémoire transactionnelle Mémoire transactionnelle

Transaction - exemple Transaction - exemple


Invariant x = y

Invariant x + y = z T1 T2
1. x ← x + 100 α. x ← x ∗ 2
T1 T2 2. y ← y + 100 β. y ← y ∗ 2
1. a1 ← x α. c2 ← z
2. b1 ← y β. z ← c2 + 200 OK : h1 · 2 · α · βi, h1 · α · 2 · βi, . . .
3. x ← a1 − 100 γ. d2 ← x KO : h1 · α · β · 2i, hα · 1 · 2 · βi
4. y ← b1 + 100 δ. x ← d2 + 200
T1 T2
OK : h1 · 2 · α · β · 3 · γ · 4 · δi car ≡ T 1; T 2 (sérialisable) 1. x ← x + 100 α. x ← x ∗ 2
KO : h1 · 2 · α · β · γ · 3 · 4 · δi car ≡
6 T 1; T 2 et 6≡ T 2; T 1 2. y ← y + 100 β. y ← x

OK : h1 · 2 · α · βi, h1 · α · 2 · βi, hα · 1 · 2 · βi, . . .


KO : h1 · α · β · 2i
Systèmes concurrents – Transactions 8 / 59 Systèmes concurrents – Transactions 9 / 59
Transaction Interférences entre actions Transaction Interférences entre actions
Atomicité/Tout ou rien Définition des transactions Atomicité/Tout ou rien Définition des transactions
Contrôle de concurrence Type d’incohérences Contrôle de concurrence Type d’incohérences
Mémoire transactionnelle Mémoire transactionnelle

Domaines d’utilisation Propriétés ACID

Propriétés ACID
Systèmes d’information : bases de données Atomicité ou  tout ou rien  : en cas d’abandon (volontaire
ou subi), aucun effet visible
Systèmes de fichiers (en particulier systèmes journalisés)
Cohérence : respect des contraintes d’intégrité
Mémoire transactionnelle
(HTM/STM = hardware/software transactional memory) Isolation : pas d’interférences entre transactions = pas d’états
intermédiaires observables
Durabilité : permanence des effets d’une transaction validée

Systèmes concurrents – Transactions 10 / 59 Systèmes concurrents – Transactions 11 / 59

Transaction Interférences entre actions Transaction Interférences entre actions


Atomicité/Tout ou rien Définition des transactions Atomicité/Tout ou rien Définition des transactions
Contrôle de concurrence Type d’incohérences Contrôle de concurrence Type d’incohérences
Mémoire transactionnelle Mémoire transactionnelle

Annulation/abandon Service transactionnel

Interface du service :
tdébut()/tfin() : parenthésage des opérations
Pour garantir la cohérence et/ou l’isolation ⇒ possibilité transactionnelles
d’abandon (abort) d’un traitement en cours, décidé par le
tabandon() : annulation des effets de la transaction
système de gestion.
técrire(...), tlire(...) : accès aux données.
Du coup, autant le fournir aussi au programmeur.
(Opérations éventuellement implicites, mais dont l’observation
est nécessaire au service transactionnel pour garantir la
cohérence)

Systèmes concurrents – Transactions 12 / 59 Systèmes concurrents – Transactions 13 / 59


Transaction Interférences entre actions Transaction Interférences entre actions
Atomicité/Tout ou rien Définition des transactions Atomicité/Tout ou rien Définition des transactions
Contrôle de concurrence Type d’incohérences Contrôle de concurrence Type d’incohérences
Mémoire transactionnelle Mémoire transactionnelle

Incohérences Incohérences
Pertes de mises à jour Lectures non répétables
Écritures écrasées par d’autres écritures. Donnée qui change de valeur

(1) a := lire(x) ;
(1) a := lire(x) ;
(a) écrire(x,100) ;
(a) b := lire(x) ;
(2) b := lire(x) ;
(2) écrire(x,a+10) ;
(b) écrire(x, b+20) ;
Lectures fantômes
Lectures sales Donnée agrégée qui change de contenu

Écritures abandonnées mais observées (0) sum := 0 ;


(1) nb := cardinal(S)
(1) écrire(x,100) ; (a) ajouter(S, 15) ;
(a) b := lire(x) ; (2) ∀ x ∈ S : sum := sum + x
(2) abandon ; (3) moyenne := sum / nb

Systèmes concurrents – Transactions 14 / 59 Systèmes concurrents – Transactions 15 / 59

Transaction Interférences entre actions Transaction


Atomicité/Tout ou rien Définition des transactions Atomicité/Tout ou rien
Contrôle de concurrence Type d’incohérences Contrôle de concurrence
Mémoire transactionnelle Mémoire transactionnelle

Comment évaluer la cohérence efficacement ? Plan


1 Transaction
Interférences entre actions
Objectif
Définition des transactions
Eviter d’évaluer la cohérence globalement, et à chaque instant Type d’incohérences
2 Atomicité/Tout ou rien
Evaluation épisodique/périodique (après un ensemble de pas)
→ pouvoir annuler un ensemble de pas en cas d’incohérence 3 Contrôle de concurrence
Principe
Evaluation approchée : trouver une condition suffisante, plus
Modélisation
simple à évaluer (locale dans l’espace ou dans le temps)
Méthodes
→ notions de sérialisabilité et de conflit (cf infra)
Relâcher les exigences de cohérence, afin d’avoir des critères
4 Mémoire transactionnelle
locaux, plus simples à évaluer Intégration dans un langage
Difficultés
Implantation : STM, HTM

Systèmes concurrents – Transactions 16 / 59 Systèmes concurrents – Transactions 17 / 59


Transaction Transaction
Atomicité/Tout ou rien Atomicité/Tout ou rien
Contrôle de concurrence Contrôle de concurrence
Mémoire transactionnelle Mémoire transactionnelle

Atomicité (tout ou rien) Abandon sans effet

Objectif
Intégrer les résultats des transactions  bien  terminées Comment abandonner une transaction sans effet ?
Assurer qu’une transaction annulée n’a aucun effet sur les 1 Pessimiste / propagation différée : mémoire temporaire
données partagées transférée en mémoire définitive à la validation (redo-log)
2 Optimiste / propagation immédiate (en continu) : écriture
Difficulté directe avec sauvegarde de l’ancienne valeur ou de l’action
Tenir compte de la possibilité de pannes en cours inverse (journaux / undo-log)
d’exécution, Effet domino : T 0 observe une écriture de T puis T
abandonne ⇒ T 0 doit être abandonnée
ou d’enregistrement des résultats définitifs,
ou d’annulation.

Systèmes concurrents – Transactions 18 / 59 Systèmes concurrents – Transactions 19 / 59

Transaction Transaction
Atomicité/Tout ou rien Atomicité/Tout ou rien
Contrôle de concurrence Contrôle de concurrence
Mémoire transactionnelle Mémoire transactionnelle

Mise en œuvre de l’atomicité Approche pessimiste : propagation différée


Opérations de base Utilisation d’un journal des valeurs après
défaire : revenir à l’état initial d’une transaction annulée
refaire : reprendre une validation interrompue par une panne Principe
Écriture dans un espace de travail privé, en mémoire volatile
Réalisation de défaire et refaire → adapté aux mécanismes de gestion mémoire (caches. . . )
Basée sur la gestion d’un journal, conservé en mémoire stable. Journalisation de la validation

Contenu d’un enregistrement du journal : écrire → préécriture dans l’espace de travail


[date, id. transaction, id. objet, valeur avant (et/ou valeur après)] valider → recopier l’espace de travail en mémoire stable
Utilisation des journaux (liste d’intentions), puis copier celle-ci en mémoire
défaire → utiliser les valeurs avant pour revenir à l’état initial permanente
refaire → utiliser les valeurs après pour rétablir l’état atteint → protection contre les pannes en cours de validation
Remarque : en cas de panne durant une opération défaire ou défaire → libérer l’espace de travail
refaire, celle-ci peut être reprise du début. refaire → reprendre la recopie de la liste d’intentions
Systèmes concurrents – Transactions 20 / 59 Systèmes concurrents – Transactions 21 / 59
Transaction Transaction Principe
Atomicité/Tout ou rien Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Approche optimiste : propagation en continu Plan


Utilisation d’un journal des valeurs avant 1 Transaction
écrire → écriture directe en mémoire permanente Interférences entre actions
valider → effacer les images avant Définition des transactions
défaire → utiliser le journal avant Type d’incohérences
refaire → sans objet (validation sans pb) 2 Atomicité/Tout ou rien
Problèmes liés aux abandons 3 Contrôle de concurrence
Rejets en cascade Principe
(1) técrire(x,10) (2) tlire(x) Modélisation
(3) técrire(y,8)) Méthodes
(4) tabandon() → abandonner aussi
4 Mémoire transactionnelle
Perte de l’état initial Intégration dans un langage
initialement : x=5 Difficultés
(1) técrire(x,10) (2) técrire(x,8)) Implantation : STM, HTM
(3) tabandon() (4) tabandon() → x=10 au lieu de x=5
Systèmes concurrents – Transactions 22 / 59 Systèmes concurrents – Transactions 23 / 59

Transaction Principe Transaction Principe


Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Contrôle de concurrence Sémantique


Single-lock atomicity : exécution équivalente à l’utilisation
Objectif d’un unique verrou global.
Assurer une protection contre les interférences entre transactions Sérialisabilité : résultat final équivalent à une exécution
identique à celle obtenue avec l’exclusion mutuelle, tout en sérialisée des transactions qui valident.
autorisant une exécution concurrente (autant que possible) Sérialisabilité stricte : sérialisabilité + respect de l’ordre temps
réel (si TA termine avant que TB ne démarre et que les deux
valident, TA doit apparaı̂tre avant TB dans l’exécution
1 Exécution sérialisée : isolation par exclusion mutuelle.
sérialisée équivalente)
2 Exécution sérialisable : contrôler l’entrelacement des actions Linéarisabilité : transaction considérée comme une opération
pour que l’effet final soit équivalent à une exécution sérialisée. atomique instantanée, à un point entre son début et sa
validation.
Il peut exister plusieurs exécutions sérialisées équivalentes
(différence avec sérialisabilité : accès non transactionnels pris
Contrôle automatique en compte)
Opacité : sérialisabilité stricte, y compris des transactions
annulées (indépendance par rapport aux transactions actives).
Systèmes concurrents – Transactions 24 / 59 Systèmes concurrents – Transactions 25 / 59
Transaction Principe Transaction Principe
Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Conflits Autres conflits

Conflit : opérations non commutatives exécutées sur un même


objet La notion de conflit n’est pas spécifique aux opérations Lire/Écrire.

Exemple Exemple
Avec opérations Lire(x) et Écrire(x,v) : lire écrire incrémenter décrémenter
lire OK – – –
conflit LL : non écrire – – – –
conflit LE : T1 .lire(x) ; . . . ; T2 .écrire(x,n) ; incrémenter – – OK OK
conflit EL : T1 .écrire(x,n) ; . . . ; T2 .lire(x) ; décrémenter – – OK OK
conflit EE : T1 .écrire(x,n) ; . . . ; T2 .écrire(x,n’) ;

Systèmes concurrents – Transactions 26 / 59 Systèmes concurrents – Transactions 27 / 59

Transaction Principe Transaction Principe


Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Graphe de dépendance, sérialisabilité Exemple

Définition
Relation de dépendance → : T1 → T2 ssi une opération de T1
précède et est en conflit avec une opération de T2 .

Définition
Graphe de dépendance : relations de dépendance pour les
transactions déjà validées.

Théorème
Exécution sérialisable : une exécution est sérialisable si son graphe
de dépendance est acyclique.

Systèmes concurrents – Transactions 28 / 59 Systèmes concurrents – Transactions 29 / 59


Transaction Principe Transaction Principe
Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Moins que sérialisable ? Contrôle de concurrence

Quand vérifier la sérialisabilité :


La sérialisabilité est parfois un critère trop fort : cohérence faible. 1 à chaque terminaison d’une transaction
SQL définit quatre niveaux d’isolation : (contrôle par certification ou optimiste)
Serializable : cohérence complète 2 à chaque nouvelle dépendance
Repeatable read : lectures fantômes acceptées (contrôle continu ou pessimiste)
Read committed : lectures non répétables ou fantômes
acceptées Comment garantir la sérialisabilité :
Read uncommitted : lectures sales, non répétables ou 1 Utilisation explicite du graphe de dépendance
fantômes acceptées 2 Fixer/observer un ordre sur les transactions qui garantit
l’absence de cycle : estampilles, verrous

Systèmes concurrents – Transactions 30 / 59 Systèmes concurrents – Transactions 31 / 59

Transaction Principe Transaction Principe


Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Certification (concurrence explicite) Certification (estampille)


Algorithme
Algorithme
-- écritures en mémoire privée avec recopie à la validation
-- écritures en mémoire privée avec recopie à la validation C : nbre de transactions certifiées
T.lus, T.écrits : objets lus/écrits par T T.déb : valeur de C au début de T
T.concur : transactions ayant validé pendant l’exéc. de T T.fin : valeur de C à la fin de T
actives : ensemble des transactions en cours d’exécution T.val : valeur de C si T certifiée
procédure Certifier(T) : T.lus, T.écrits : objets lus/écrits par T
si (∀ T’ ∈ T.concur : T.lus ∩ T’.écrits = ∅) procédure Certifier(T) :
alors si (∀ T’ : T.déb < T’.val < T.fin : T.lus ∩ T’.écrits = ∅)
-- T peut valider alors
∀ T’ ∈ actives : T’.concur ← T’.concur ∪ {T} C ← C + 1
sinon T.val ← C
-- abandon de T sinon
fin abandon de T
Protocole coûteux et coût du rejet ⇒ faible taux de conflit fin
Protocole coûteux et coût du rejet ⇒ faible taux de conflit
Systèmes concurrents – Transactions 32 / 59 Systèmes concurrents – Transactions 33 / 59
Transaction Principe Transaction Principe
Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Contrôle continu par estampilles Estampilles : abandon par prudence


Ordre de sérialisation = ordre des estampilles
Algorithme
Abandon superflu
T.E : estampille de T
O.lect : estampille du plus récent lecteur de O T1.E = 1 T2.E = 2
O.réd : estampille du plus récent écrivain de O
lire z
procédure lire(T,O) procédure écrire(T,O,v) écrire x
si T.E ≥ O.réd si T.E ≥ O.lect ∧ T.E ≥ O.réd
écrire x → abandon de T1
alors alors
lecture de O possible écriture de O possible ? écrire z
O.lect ← max(O.lect,T.E) O.red ← T.E
sinon sinon L’abandon n’est nécessaire que s’il y aura conflit effectif
abandon de T abandon de T ultérieurement. Dans le doute, abandon.
finsi finsi

Estampille fixée au démarrage de la transaction ou au 1er conflit.


Systèmes concurrents – Transactions 34 / 59 Systèmes concurrents – Transactions 35 / 59

Transaction Principe Transaction Principe


Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Estampilles : amélioration Contrôle continu par verrouillage à deux phases


Réduire les cas d’abandons. Exemple : règle de Thomas
Ordre de sérialisation = ordre chronologique d’accès aux objets
Algorithme
procédure écrire(T,O,v) T1 → T2 : bloquer T2 jusqu’à ce que T1 valide.
si T.E ≥ O.lect Verrous en lecture/écriture/. . .
alors
action sérialisable : écriture possible Si toute transaction est
si T.E ≥ O.réd bien formée (prise du verrou avant une opération)
écriture effective
à deux phases (pas de prise de verrou après une libération)
O.red ← T.E
sinon phase 1 : acquisitions et opérations
rien : écriture écrasée par transaction plus récente point de validation
finsi phase 2 : libérations
sinon
abandon de T
alors la sérialisation est assurée.
finsi Note : et l’interblocage ? Cf gestion de la contention.
Systèmes concurrents – Transactions 36 / 59 Systèmes concurrents – Transactions 37 / 59
Transaction Principe Transaction Principe
Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Nécessité des deux phases Verrouillage à deux phases : justification du protocole


L’utilisation simple de verrous (sans règle des deux phases) ne Idée de base
suffit pas à assurer la sérialisation.
Lorsque deux transactions sont en conflit, toutes les paires
Invariant x = y d’opérations en conflit sont exécutées dans le même ordre
→ pas de dépendances d’orientation opposée → pas de cycle
T1 T2 Schéma de preuve
1. lock x α. lock x
Notation :
2. x ←x +1 β. lock y
e1 ≺ e2 ≡ l’événement e1 s’est produit avant l’événement e2
3. unlock x γ. x ←x ∗2
4. lock y δ. y ←y ∗2 Ti → Tj ⇒ ∃O1 : Ti .libérer(O1 ) ≺ Tj .verrouiller(O1 )
5. y ←y +1 . unlock y Tj → Ti ⇒ ∃O2 : Tj .libérer(O2 ) ≺ Ti .verrouiller(O2 )
6. unlock y ζ. unlock x Ti à deux phases ⇒ Ti .verrouiller(O2 ) ≺ Ti .libérer(O1 )
donc, Tj n’est pas à deux phases (contradiction), car :
KO : h1 · · · 3 · α · · · ζ · 4 · · · 6i
Tj .libérer(O2 ) ≺ Ti .verrouiller(O2 ) ≺ Ti .libérer(O1 ) ≺ Tj .verrouiller (O1 )
Systèmes concurrents – Transactions 38 / 59 Systèmes concurrents – Transactions 39 / 59

Transaction Principe Transaction Principe


Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Verrouillage à deux phases Verrouillage strict à deux phases

Condition suffisante ⇒ restrictions  inutiles  du parallèlisme


Que faire en cas de conflit de verrouillage : Prise implicite du verrou au premier accès à une variable
Abandon systématique ⇒ famine
Libération automatique à la validation/abandon
Blocage systématique ⇒ interblocage
ordre sur la prise des verrous (classes ordonnées)
prédéclaration de tous les verrous nécessaires Garantit simplement les deux phases
(pour les prendre tous ensemble atomiquement)
Tout se fait à la validation : simple
Soit un conflit T1 → T2
wait-die : si T2 a démarré avant T1 , alors bloquer T2 Restriction du parallélisme
sinon abandonner T2 . (conservation des verrous jusqu’à la fin)
wound-wait : si T2 a démarré avant T1 alors abandonner T1
sinon bloquer T2 .

Systèmes concurrents – Transactions 40 / 59 Systèmes concurrents – Transactions 41 / 59


Transaction Principe Transaction Principe
Atomicité/Tout ou rien Modélisation Atomicité/Tout ou rien Modélisation
Contrôle de concurrence Méthodes Contrôle de concurrence Méthodes
Mémoire transactionnelle Mémoire transactionnelle

Gestionnaire de contention Conclusion

Chaque méthode a son contexte d’application privilégié


Contention Paramètres déterminants
En cas de conflit : taux de conflit
durée des transactions
1 quelle transaction bloquer ?
Résultats
2 quelle transaction annuler ?
peu de conflits → méthodes optimistes
3 éventuellement quelle transaction redémarrer et quand ? nombreux conflits/transactions longues
→ verrouillage à deux phases
Garantir la progression optimale et l’absence d’interblocage ⇒ situation intermédiaire pour l’estampillage
d’innombrables stratégies. Simplicité de mise en œuvre du verrouillage à deux phases
→ choix le plus courant en base de données

Systèmes concurrents – Transactions 42 / 59 Systèmes concurrents – Transactions 43 / 59

Transaction Intégration dans un langage Transaction Intégration dans un langage


Atomicité/Tout ou rien Difficultés Atomicité/Tout ou rien Difficultés
Contrôle de concurrence Implantation : STM, HTM Contrôle de concurrence Implantation : STM, HTM
Mémoire transactionnelle Mémoire transactionnelle

Plan Mémoire transactionnelle


1 Transaction
Interférences entre actions
Définition des transactions
Type d’incohérences Introduire la notion de transactions au niveau du langage de
2 Atomicité/Tout ou rien programmation
3 Contrôle de concurrence Objectif : se passer des verrous habituellement utilisés pour
protéger les variables partagées
Principe
plus nécessaire d’identifier la bonne granularité des verrous
Modélisation
interblocage, etc : c’est le problème de la STM
Méthodes gestion des priorité, etc : idem
4 Mémoire transactionnelle
Intégration dans un langage
Difficultés
Implantation : STM, HTM

Systèmes concurrents – Transactions 44 / 59 Systèmes concurrents – Transactions 45 / 59


Transaction Intégration dans un langage Transaction Intégration dans un langage
Atomicité/Tout ou rien Difficultés Atomicité/Tout ou rien Difficultés
Contrôle de concurrence Implantation : STM, HTM Contrôle de concurrence Implantation : STM, HTM
Mémoire transactionnelle Mémoire transactionnelle

Intégration explicite dans un langage Intégration dans un langage

Introduire un bloc atomique :


Exposer l’interface de manipulation des transactions et des accès.
atomically
Interface exposée
atomically {
do {
x = y + 2;
tx = StartTx();
y = x + 3;
int v = tx.ReadTx(&x);
}
tx.WriteTx(&y, v+1);
} while (! tx.CommitTx()); (analogue aux régions critiques, sans déclaration explicite des
variables partagées)

Systèmes concurrents – Transactions 46 / 59 Systèmes concurrents – Transactions 47 / 59

Transaction Intégration dans un langage Transaction Intégration dans un langage


Atomicité/Tout ou rien Difficultés Atomicité/Tout ou rien Difficultés
Contrôle de concurrence Implantation : STM, HTM Contrôle de concurrence Implantation : STM, HTM
Mémoire transactionnelle Mémoire transactionnelle

Transaction et synchronisation Transaction et synchronisation 2


Synchronisation ⇒ blocage ⇒ absence de progression de la
transaction ⇒ absence de progression d’autres transactions ⇒
interblocage. Exécuter une autre transaction si la première échoue :
Intégrer la synchronisation dans les transactions
orelse
Abandonner la transaction pour la redémarrer automatiquement
quand certaines des valeurs lues auront changé. atomically {
// consommer dans le tampon 1
retry }
procédure consommer orelse
atomically {
atomically {
if (nbÉlémentsDisponibles > 0) {
// choisir un élément et l’extraire
// consommer dans le tampon 2
nbÉlémentsDisponibles-- }
} else {
retry;
}
}
Systèmes concurrents – Transactions 48 / 59 Systèmes concurrents – Transactions 49 / 59
Transaction Intégration dans un langage Transaction Intégration dans un langage
Atomicité/Tout ou rien Difficultés Atomicité/Tout ou rien Difficultés
Contrôle de concurrence Implantation : STM, HTM Contrôle de concurrence Implantation : STM, HTM
Mémoire transactionnelle Mémoire transactionnelle

Cohérence interne Interaction avec code non transactionnel


Sémantique définie sur les transactions validées (sérialisabilité) ou Lectures non répétables
toutes (opacité) ? Donnée qui change de valeur

init x=y atomic {


a := lire(x) ;
atomic { atomic {
écrire(x,100) ;
if (x != y) x++; b := lire(x) ;
while (true) {} y++; }
} }
int *x; bool nonnul; Lectures sales
atomic { atomic {
Écritures abandonnées mais observées
if (nonnul) x ← NULL;
*x ← 3; nonnul ← false; atomic {
} } écrire(x,100) ;
b := lire(x) ;
Transaction zombie (ou condamnée) mais visible. abandon ;
Systèmes concurrents – Transactions 50 / 59
}
Systèmes concurrents – Transactions 51 / 59

Transaction Intégration dans un langage Transaction Intégration dans un langage


Atomicité/Tout ou rien Difficultés Atomicité/Tout ou rien Difficultés
Contrôle de concurrence Implantation : STM, HTM Contrôle de concurrence Implantation : STM, HTM
Mémoire transactionnelle Mémoire transactionnelle

Actions non annulables Transaction et exception


Exception dans une transaction ?
Une transaction annulée doit être sans effet : comment faire s’il y a Une transaction est une séquence tout ou rien de code ;
des effets de bords (p.e. entrées/sorties) ? La levée d’une exception dans un bloc doit sortir
immédiatement du bloc.
1 Interdire : uniquement des lectures/écritures de variables.
2 Ignorer le problème en considérant ces opérations comme des 1 Valider la transaction : considérer l’exception comme une
nop, et tant pis si la transaction est annulée. branche conditionnelle.
3 Irrévocabilité : quand une transaction invoque une action non Simple à mettre en œuvre, ne change pas la sémantique d’un
défaisable/non retardable, la transaction devient irrévocable : code séquentiel.
ne peut plus être annulée une fois l’action effectuée. 2 Annuler la transaction : considérer l’exception comme abort.
Simplifie la composition et l’utilisation de librairie : dans
4 Intégrer dans le système transactionnel : monade d’IO
atomic { s.foo(); s.bar();}, si bar échoue à cause
d’Haskell
d’une exception, rien n’a eu lieu.
Mais si l’exception est due au code de la transaction, la cause
de l’exception disparaı̂t à l’annulation !
Systèmes concurrents – Transactions 52 / 59 Systèmes concurrents – Transactions 53 / 59
Transaction Intégration dans un langage Transaction Intégration dans un langage
Atomicité/Tout ou rien Difficultés Atomicité/Tout ou rien Difficultés
Contrôle de concurrence Implantation : STM, HTM Contrôle de concurrence Implantation : STM, HTM
Mémoire transactionnelle Mémoire transactionnelle

Imbrication STM – Software Transactional Memory


Implantation purement logicielle de la mémoire transactionnelle.
Transaction imbriquée
Interface explicite
Transaction s’exécutant dans le contexte d’une transaction parente.
StartTx, CommitTx, AbortTx
ReadTx(T *addr), WriteTx(T *addr, T v),
init x = 1
atomic{ x ← 2; atomic{ x ← x+1; abort/commit;} ...} Programmation explicite, ou insertion par le compilateur.
Points critiques
1 Une seule transaction fille active ou plusieurs (parallélisme) ? Connaissances des accès read-set, write-set
2 Dans la fille, visibilité des effets de la transaction parente ? Journal ⇒ copie supplémentaire (undo-log, redo-log) ou
3 L’annulation de la fille entraı̂ne l’annulation de la parente ? double indirection (shadow copy)
4 Les effets de la fille sont-ils visibles dès sa validation (ou Meta-data associées à chaque objet élémentaire ⇒ granularité
seulement lors de la validation de la parente) ? Efficacité
À plat : 3 oui, 4 non ; fermée : 3 non, 4 non ; ouverte : 3 non, 4 oui. Nombreuses implantations, beaucoup de variété.
Systèmes concurrents – Transactions 54 / 59 Systèmes concurrents – Transactions 55 / 59

Transaction Intégration dans un langage Transaction Intégration dans un langage


Atomicité/Tout ou rien Difficultés Atomicité/Tout ou rien Difficultés
Contrôle de concurrence Implantation : STM, HTM Contrôle de concurrence Implantation : STM, HTM
Mémoire transactionnelle Mémoire transactionnelle

HTM – Hardware Transactional Memory HTM – limitations

Instructions processeur
begin transaction, end transaction
Accès explicite (load/store transactional) ou implicite Pas de changement de contexte pendant une transaction
(tous) Petites transactions (2 ou 4 mots mémoire)
Granularité fixée = unité d’accès (1 mot)
Accès implicite ⇒ code de bibliothèque automatiquement pris en
compte + isolation forte Faux conflits dus à la granularité mot ↔ ligne de cache
Grande variété des propositions, sémantique incertaine
Implantation
Portabilité d’un code prévu pour une implantation ?
read/write-set : pratiquement le rôle du cache
détection des conflits ≈ cohérence des caches
undo/redo-log : dupliquer le cache

Systèmes concurrents – Transactions 56 / 59 Systèmes concurrents – Transactions 57 / 59


Transaction Intégration dans un langage Transaction Intégration dans un langage
Atomicité/Tout ou rien Difficultés Atomicité/Tout ou rien Difficultés
Contrôle de concurrence Implantation : STM, HTM Contrôle de concurrence Implantation : STM, HTM
Mémoire transactionnelle Mémoire transactionnelle

Implantation hybride Conclusion


Coopération STM/HTM
Petites transactions en HTM, grosses en STM + simple à appréhender
Problème : détection d’inadéquation de l’HTM, basculement ?
+ réduction des bugs de programmation
Problème : sémantiques différentes
+ nombreuses implantations portables en logiciel
Implantation STM sur HTM
+ compatible avec la programmation événementielle
Une HTM pour petites transactions
− nombreuses sémantiques, souvent floues (mais ce n’est pas
Implantation de la STM avec les transactions matérielles
pire que les modèles de mémoires partagées)
HTM non visible à l’extérieur
− surcoût d’exécution
Implantation STM avec assistance matérielle
− effet  polluant  des transactions
Identifier les bons composants élémentaires nécessaires ⇒
− questions ouvertes : code hors transaction, composition,
implantation matérielle
synchronisation
Cf Multithread / contexte CPU ou Mémoire virtuelle / MMU
Encore à creuser
Systèmes concurrents – Transactions 58 / 59 Systèmes concurrents – Transactions 59 / 59
Objectifs et principes Objectifs et principes
Exemples Exemples
Conclusion Conclusion

Plan

Neuvième partie 1 Objectifs et principes

2 Exemples
Synchronisation non bloquante Splitter & renommage
Pile chaı̂née
Liste chaı̂née

3 Conclusion

Systèmes concurrents 2 / 28 Systèmes concurrents – synchronisation non bloquante 3 / 28

Objectifs et principes Objectifs et principes


Exemples Exemples
Conclusion Conclusion

Objectifs de la synchronisation non bloquante Synchronisation non bloquante

Problème Non-blocking synchronization


Garantir la cohérence d’accès à un objet partagé sans blocage Obstruction-free Si à tout point, une activité en isolation parvient
à terminer en temps fini (en un nombre fini de pas).
Résistance à l’arrêt (crash) d’une activité : une activité Lock-free Synchronisation et protection garantissant la
donnée n’est jamais empêchée de progresser, quel que soit le progression du système même si un processus s’arrête
comportement des autres activités arbitrairement. Peut utiliser de l’attente active mais
Vitesse de progression indépendante des autres activités (par exemple) pas de verrous.
Absence d’interblocage et d’inversion de priorité mais
Passage à l’échelle
risque de famine individuelle (vivacité faible).
Surcoût négligeable de synchronisation en cas d’absence de
Wait-free Une sous-classe de lock-free où tout processus est
conflit (notion de fast path)
certain de compléter son action en temps fini,
Compatible avec la programmation événementielle (un indépendamment du comportement des autres
gestionnaire d’interruption ne doit pas être bloqué par la processus (arrêté ou agressivement interférant).
synchronisation) Absence de famine individuelle (vivacité forte).
Systèmes concurrents – synchronisation non bloquante 4 / 28 Systèmes concurrents – synchronisation non bloquante 5 / 28
Objectifs et principes Objectifs et principes
Exemples Exemples
Conclusion Conclusion

Mécanismes matériels Principes généraux

Mécanismes matériels utilisés


Registres : protocoles permettant d’abstraire la gestion de la Principes
concurrence d’accès à la mémoire partagée (caches. . . ). Chaque activité travaille à partir d’une copie locale de l’objet
registres sûrs : toute lecture fournit une valeur écrite ou en partagé
cours d’écriture Un conflit est détecté lorsque la copie diffère de l’original
registres réguliers : toute lecture fournit la dernière valeur
écrite ou une valeur en cours d’écriture Boucle active en cas de conflit d’accès non résolu
registres atomiques : toute lecture fournit la dernière valeur → limiter le plus possible la zone de conflit
écrite Entraide : si un conflit est détecté, une activité peut exécuter
Instructions processeur atomiques combinant lecture(s) et des opérations pour le compte d’une autre activité
écriture(s) (exemple : test-and-set)

Systèmes concurrents – synchronisation non bloquante 6 / 28 Systèmes concurrents – synchronisation non bloquante 7 / 28

Objectifs et principes Splitter & renommage Objectifs et principes Splitter & renommage
Exemples Pile chaı̂née Exemples Pile chaı̂née
Conclusion Liste chaı̂née Conclusion Liste chaı̂née

Plan Splitter
Moir, Anderson 1995
x processus

1 Objectifs et principes
stop
right
<= x−1 processus
2 Exemples <= 1 processus

Splitter & renommage


Pile chaı̂née down

Liste chaı̂née <= x−1 processus

x (indéterminé) activités appellent concurremment (ou pas) le


3 Conclusion splitter
au plus une activité termine avec stop
si x = 1, l’activité termine avec stop
au plus (x − 1) activités terminent avec right
au plus (x − 1) activités terminent avec down
Systèmes concurrents – synchronisation non bloquante 8 / 28 Systèmes concurrents – synchronisation non bloquante 9 / 28
Objectifs et principes Splitter & renommage Objectifs et principes Splitter & renommage
Exemples Pile chaı̂née Exemples Pile chaı̂née
Conclusion Liste chaı̂née Conclusion Liste chaı̂née

Splitter Schéma de preuve


 Registres  Validité les seules valeurs retournées sont right, stop et down.
Lectures et écritures atomiques Vivacité ni boucle ni blocage
stop si x = 1 évident (une seule activité exécute direction())
Pas d’interférence due aux caches en multi-processeurs
au plus x − 1 right les activités obtenant right trouvent Y , qui a
Implantation non bloquante nécessairement été positionné par une activité
obtenant down ou stop
Deux  registres  partagés : X (init ∀) et Y (init faux)
au plus x − 1 down soit pi la dernière activité ayant écrit X . Si pi
Chaque activité a un identifiant unique idi .
trouve Y , elle obtiendra right. Sinon son test X = idi
direction(idi ) lui fera obtenir stop.
X ← idi ; au plus 1 stop soit pi la première activité trouvant X = idi . Alors aucune
if Y then diri ← right; activité n’a modifié X depuis que pi l’a fait. Donc toutes les
else Y ← true; activités suivantes trouveront Y et obtiendront right (car pi a
if (X = idi ) then diri ← stop; positionné Y ), et les activités en cours qui n’ont pas trouvé Y
else diri ← down; endif ont vu leur écriture de X écrasée par pi (puisqu’elle n’a pas
endif changé jusqu’au test par pi ). Elles ne pourront donc trouver X
return diri ; égal à leur identifiant et obtiendront donc down.

Systèmes concurrents – synchronisation non bloquante 10 / 28 Systèmes concurrents – synchronisation non bloquante 11 / 28

Objectifs et principes Splitter & renommage Objectifs et principes Splitter & renommage
Exemples Pile chaı̂née Exemples Pile chaı̂née
Conclusion Liste chaı̂née Conclusion Liste chaı̂née

Renommage Grille de splitters


Soit n activités d’identité id1 , . . . , idn ∈ [0..N] où N  n
On souhaite renommer les activités pour qu’elles aient une Étiquettes uniques : un splitter
identité prise dans [0..M] où M  N renvoie stop à une activité au
Deux activités ne doivent pas avoir la même identité plus

Solution à base de verrous Vivacité : traversée d’un nombre


Distributeur de numéro accédé en exclusion mutuelle fini de splitters, chaque splitter
M=n est non bloquant
Complexité temporelle : O(1) pour un numéro, O(n) pour tous
Toute activité obtient une étiquette :
Une activité lente ralentit les autres
stop si x = 1,
Solution non bloquante un splitter ne peut orienter toutes les activités dans la même
Grille de splitters direction,
M = n(n+1)
2 les bords de la grille sont à distance n − 1 de l’origine.
Complexité temporelle : O(n) pour un numéro, O(n) pour tous
Systèmes concurrents – synchronisation non bloquante 12 / 28 Systèmes concurrents – synchronisation non bloquante 13 / 28
Objectifs et principes Splitter & renommage Objectifs et principes Splitter & renommage
Exemples Pile chaı̂née Exemples Pile chaı̂née
Conclusion Liste chaı̂née Conclusion Liste chaı̂née

Renommage non bloquant Pile chaı̂née basique

get name(idi ) class Node<T> {


Node<T> next;
di ← 0; ri ← 0; termi ← false; T item;
while (¬termi ) do }
X [di , ri ] ← idi ;
if Y [di , ri ] then ri ← ri + 1; % right class Stack<T> {
Node<T> top;
else Y [di , ri ] ← true;
if (X [di , ri ] = idi ) then termi ← true; % stop public void push(T item) { public T pop(void) {
else di ← di + 1; % down Node<T> newTop Node<T> oldTop = top;
endif = new Node<>(item); if (oldTop == null)
endif Node<T> oldTop = top; return null;
newTop.next = oldTop; top = oldTop.next;
endwhile top = newTop; return oldTop.item;
return (n ∗ di + ri − (di (di − 1)/2)) } }
% le nom en position di , ri de la grille

Systèmes concurrents – synchronisation non bloquante 14 / 28


}
Systèmes concurrents – synchronisation non bloquante 15 / 28

Objectifs et principes Splitter & renommage Objectifs et principes Splitter & renommage
Exemples Pile chaı̂née Exemples Pile chaı̂née
Conclusion Liste chaı̂née Conclusion Liste chaı̂née

Synchronisation classique Compare-and-set


Conflit push/push, pop/pop, push/pop ⇒ exclusion mutuelle Hypothèses
public T pop(void) { Lectures et écritures atomiques ( registres ), sans
verrou.lock(); interférence due aux caches en multi-processeurs
public void push(T item) {
try {
verrou.lock(); → AtomicReference
Node<T> oldTop = top;
Node<T> newTop Une instruction atomique évoluée : compare-and-set
if (oldTop == null)
= new Node<>(item);
return null;
Node<T> oldTop = top; compare-and-set
top = oldTop.next;
newTop.next = oldTop; boolean CAS(*add, old, new) {
return oldTop.item;
top = newTop; atomically {
} finally {
verrou.unlock(); if (*add == old ) {
verrou.unlock();
} *add = new;
}
} return true;
} else {
return false;
Bloquant définitivement si une activité s’arrête en plein milieu }
Toutes les activités sont ralenties par un unique lent }
Systèmes concurrents – synchronisation non bloquante 16 / 28 } concurrents – synchronisation non bloquante
Systèmes 17 / 28
Objectifs et principes Splitter & renommage
Push/pop lock free Exemples Pile chaı̂née
Liste chaı̂née
Conclusion

class Stack<T> { File basique


AtomicReference<Node<T>> top = new AtomicReference<Node<T>>();
class Node<T> { Node<T> next; T item; }
public void push(T item) {
Node<T> newTop = new Node<>(item); class File<T> {
Node<T> oldTop; Node<T> head, queue;
do {
oldTop = top.get(); File() { // nœud bidon en t^
ete
newTop.next = oldTop; head = queue = new Node<T>();
} while (! top.compareAndSet(oldTop, newTop)); }
}
public T pop(void) { T dequeue () {
Node<T> oldTop, newTop; void enqueue (T item) { T rés = null;
do { Node<T> n = new Node<T>(); if (head != queue) {
oldTop = top.get(); n.item = item; head = head.next;
if (oldTop == null) queue.next = n; res = head.item;
return null; queue = n; }
newTop = oldTop.next; } return res;
} while (! top.compareAndSet(oldTop, newTop)); }
return oldTop.item; Systèmes concurrents – synchronisation non bloquante 19 / 28
}

Objectifs et principes Splitter & renommage Objectifs et principes Splitter & renommage
Exemples Pile chaı̂née Exemples Pile chaı̂née
Conclusion Liste chaı̂née Conclusion Liste chaı̂née

Synchronisation classique Enfiler non bloquant


Conflit enfiler/enfiler, retirer/retirer, enfiler/retirer Enfiler non bloquant
⇒ tout en exclusion mutuelle Node<T> n = new Node<T>;
n.item = item;
T dequeue () { loop
void enqueue (T item) { T res = null; Node<T> lqueue = queue;
Node<T> n = new Node<T>(); verrou.lock(); Node<T> lnext = lqueue.next;
n.item = item; if (head != queue) { if lqueue == queue then lqueue et lnext cohérents ?
verrou.lock(); head = head.next; if lnext == null then queue vraiment dernier ?
queue.next = n; res = head.item; if CAS(lqueue.next, lnext, n) essai lien nouveau nœud
queue = n; } break; succès !
verrou.unlock(); verrou.unlock(); endif
} return res; else queue n’était pas le dernier nœud
} CAS(queue, lqueue, lnext); essai m-à-j queue
endif
Bloquant définitivement si une activité s’arrête en plein milieu endif
Toutes les activités sont ralenties par un unique lent endloop
Compétition systématique enfiler/défiler CAS(queue, lqueue, n); insertion réussie, essai m-à-j queue
Systèmes concurrents – synchronisation non bloquante 20 / 28 Systèmes concurrents – synchronisation non bloquante 21 / 28
Objectifs et principes Splitter & renommage Objectifs et principes Splitter & renommage
Exemples Pile chaı̂née Exemples Pile chaı̂née
Conclusion Liste chaı̂née Conclusion Liste chaı̂née

Défiler non bloquant Problème A-B-A


loop
Node<T> lhead = head; Node<T> lqueue = queue;
Node<T> lnext = lhead.next;
if lhead == head then lqueue, lhead, lnext cohérents ?
if lhead == lqueue then file vide ou queue à la tra^ıne ? L’algorithme précédent n’est correct qu’en absence de
if (lnext == null) then recyclage des cellules libérées par défiler
return null; file vide
Problème A-B-A :
endif
CAS(queue, lqueue, lnext); essai m-à-j queue
1 A1 lit x et obtient a
else
2 A2 change x en b et libère a
res = lnext.item;
3 A3 demande un objet libre et obtient a
if CAS(head, lhead, lnext) then essai m-à-j t^
ete
4 A3 change x en a
break; succès !
5 A1 effectue CAS(x,a,. . . ), qui réussit et lui laisse croire que x
endif n’a pas changé depuis sa lecture
endif
endif
endloop sinon (queue ou t^ete à la tra^
ıne) on recommence
return res;
Systèmes concurrents – synchronisation non bloquante 23 / 28 Systèmes concurrents – synchronisation non bloquante 25 / 28

Objectifs et principes Splitter & renommage Objectifs et principes


Exemples Pile chaı̂née Exemples
Conclusion Liste chaı̂née Conclusion

Solutions au problème A-B-A Plan

Compteur de générations, incrémenté à chaque modification


ha, gen ii =
6 ha, gen i + 2i
Nécessite un CAS2(x,a,gen,i,. . . ) 1 Objectifs et principes
(java.util.concurrent.atomic.AtomicStampedReference)
Instructions load-link / store-conditional (LL/SC) : 2 Exemples
Load-link renvoie la valeur courante d’une case mémoire Splitter & renommage
Store-conditional écrit une nouvelle valeur à condition que la
Pile chaı̂née
case mémoire n’a pas été écrite depuis le dernier load-link.
(les implantations matérielles imposent souvent des raisons Liste chaı̂née
supplémentaires d’échec de SC : imbrication de LL, écriture sur
la ligne de cache voire écriture quelconque. . . ) 3 Conclusion
Ramasse-miette découplé : retarder la réutilisation d’une
cellule (Hazard pointers). L’allocation/libération devient alors
le facteur limitant de l’algorithme.

Systèmes concurrents – synchronisation non bloquante 26 / 28 Systèmes concurrents – synchronisation non bloquante 27 / 28
Objectifs et principes
Exemples
Conclusion

Conclusion

+ performant, même avec beaucoup d’activités


+ résistant
− structure de données ad-hoc
− implantation fragile, peu réutilisable, pas extensible
− implantation très complexe, à réserver aux experts
− implantation liée à une architecture matérielle
− nécessité de prouver la correction
+ bibliothèques spécialisées
java.util.concurrent.ConcurrentLinkedQueue
j.u.concurrent.atomic.AtomicReference.compareAndSet
j.u.concurrent.atomic.AtomicInteger

Systèmes concurrents – synchronisation non bloquante 28 / 28


Conclusion

Programmation parallèle
souvent utile
Systèmes concurrents – Conclusion
parfois indispensable
fragile et complexe
Philippe Quéinnec souvent difficile
amusant

8 novembre 2017 Deux aspects


le parallélisme
la synchronisation

Systèmes concurrents – Conclusion 1/5 Systèmes concurrents – Conclusion 2/5

Approches Approches

Approches traditionnelles
création explicite d’activités Approches modernes
synchronisation explicite création implicite d’activités
mécanismes classiques (verrou d’exclusion mutuelle, synchronisation implicite
sémaphore, moniteur) schémas classiques (fork-join)
raisonnablement connues ne résolvent pas tous les problèmes
schémas classiques (producteurs/consommateurs, Exemple : OpenMP, interface Java Executor (pool de
lecteurs/rédacteurs) threads. . . ), bibliothèques avancées
Exemples : Java Thread, C POSIX Threads

Systèmes concurrents – Conclusion 3/5 Systèmes concurrents – Conclusion 4/5


Approches

Approches d’avenir ( ?)
Création implicite et explicite d’activités
Synchronisation implicite
mémoire transactionnelle
bibliothèque de structures de données non bloquantes
Absence d’effets de bord :
langages fonctionnels (Haskell)
→ parallélisation paresseuse
programmation événementielle ou dataflow

Systèmes concurrents – Conclusion 5/5