730 likes | 840 Views
Software concurrency. Fils d’exécution tournants concurremment. Évolution de la programmation. La programmation au départ était linéaire. Série d’instructions exécutées séquentiellement. Rien n’est fait simultanément ! Résultat déterministe.
E N D
Software concurrency Fils d’exécution tournants concurremment
Évolution de la programmation • La programmation au départ était linéaire. • Série d’instructions exécutées séquentiellement. • Rien n’est fait simultanément ! • Résultat déterministe. • Maintenant, la programmation implique l’exécution de tâches concurrentes partageant des variables, des ressources. • « Fil d’exécution » (en anglais thread); • Interruptions; • Résultat non-déterministe.
Évolution de la programmation • Des problèmes risquent de survenir si cette exécution concurrente n’est pas prise en compte. • Certains événements montrent que cela peut même impliquer des pertes de vies. • Exemple: Therac-25
Exemple • Supposons que nous avons deux routines concurrentes dont le contenu est identique: • La variable i est initialisée à 0. • Quelle sera la valeur de i lorsque ces deux routines concurrentes se seront exécutées ? • Est-ce que se sera 1 ou 2 ?
Exemple • Supposons que nous avons deux routines concurrentes dont le contenu est identique: • Réponse: parfois 1, parfois 2. • Impossible à prévoir. • Cela est inacceptable.
Processus léger (Thread) • Exemple en Java: • Compteurs partageant une variable.
Processus léger (Thread) • Exemple en Java:
Processus léger (Thread) • Exemple en Java:
Processus léger (Thread) • Exemple en Java: • Devrait compter jusqu’à 20 !
Processus léger (Thread) • Exemple en Java: • Compteur amélioré:
Processus léger (Thread) • Exemple en Java:
Processus léger (Thread) • Exemple en Java:
Processus léger (Thread) • Exemple en Java:
Accès à une variable partagée. • L’accès à une variable partagée peut être source de problème si plusieurs « threads » peuvent y écrire.
Sémaphores • Mise en situation: • Supposez que vous désirez absolument diner avant Bob. Comment faire pour s’assurer que cela se produise ? • On assume que Bob suivra vos instructions à la lettre.
Sémaphores • Considérez cette séquence: • Assure que a3 se produise avant b3 ! • Donc les diners seront séquentiels. • Pour ce qui est des déjeuners, ils sont fait concurremment car on ne sait pas qui déjeunera le premier.
Non-déterminisme • Comme l’exemple précédent en Java le montre deux « thread » concurrents sont non déterministes. • Ainsi: • … aura comme résultats possibles: • yes no • no yes
Non-déterminisme • Cela rend le test de programmes très difficile, car un programme « multithreads » peut fonctionner parfaitement 1000 fois de suite et flancher au 1001e essai… • Donc, à moins de tester un nombre infini de fois, on ne peut garantir qu’un tel programme n’a pas de bogues. • On ne peut que les éviter par une programmation soignée et rigoureuse !
Variables partagées • Certaines variables peuvent être partagées par plusieurs « threads ». • Façon simple de faire en sorte que les « threads » interagissent entre eux. • Un « thread » inscrit une valeur et les autres vont consulter. • Sans mécanisme de synchronisation, comment savoir si on lit la dernière valeur inscrite par le « thread » ou une ancienne valeur.
Écriture concurrente • Exemple: • Quelle valeur sera imprimée ? • 5 ou 7 ? • Quelle sera la valeur finale ? • 5 ou 7 ?
Écriture concurrente • Exemple: • A première vue, pas de bogues… • Mais, une fois compilé en assembleur, cela peut entrainer une lecture suivit d’une écriture:
Définition d’un sémaphore • Un sémaphore est une variable globale initialisée à 1 (ou tout autre valeur, selon l’application): • La fonction signal incrémente la valeur du sémaphore. • La fonction wait décrémente la valeur du sémaphore et bloque le « thread » si la valeur devient négative.
Définition d’un sémaphore • Ainsi, un « thread » restera bloqué sur un « wait », tant qu’un autre « thread » ne fera pas un « signal » pour libérer le sémaphore. • Une notification permet de libérer un « thread bloqué », même si le sémaphore est à une valeur négative. • Cela permettra de faire des synchronisations.
Sérialisation par signalisation • Soit un « thread A » contenant une instruction a1 devant obligatoirement être exécutée avant une instruction b1 d’un autre « thread » identifié B. • Solution: • Que le « thread A » signale au « thread B » la fin de l’exécution de l’instruction a1.
Rendez-vous • Voici deux « Thread »: • Comment faire pour garantir que l’instruction a1 soit exécuté avant l’instruction b2 et b1 avant a2 ? • Cela s’appelle un rendez-vous.
Rendez-vous • Solution, utiliser deux sémaphores: • Autre solution possible:
Rendez-vous mortel ! • Rendez-vous vers un interblocage:
Accès mutuellement exclusif • Appelé aussi « mutex ». • Pour rendre l’accès à des variables globales mutuellement exclusif: • L’incrément d’une variable peut donc être rendu exclusif avec un sémaphore:
Accès limité à n « threads » • C’est un « multiplex » ! • Imaginez que le sémaphore soit initialisé à 3 plutôt qu’à 1. Et supposons qu’il y ait 10 « threads » utilisant ce sémaphore comme suit:
Accès limité à n « threads » • Séquence des événements: • Thread A arrive multiplex = 2 • Exécution permise • Thread B arrive multiplex = 1 • Exécution permise • Thread C arrive multiplex = 0 • Exécution permise • Thread D arrive multiplex = -1 • Exécution bloquée • Thread E arrive multiplex = -2 • Exécution bloquée
Accès limité à n « threads » • Suite…: • Thread B terminé multiplex = -1 • Signale à un autre thread qu’il peut exécuter • Thread E débloque et exécute • Thread F arrive multiplex = -2 • Exécution bloquée • Thread C terminé multiplex = -1 • Signale à un autre thread qu’il peut exécuter • Thread D débloque et exécute • Thread E terminé multiplex = 0 • Signale à un autre thread qu’il peut exécuter • Thread F débloque et exécute
Accès limité à n « threads » • Suite…: • Thread A terminé multiplex = 1 • Thread F terminé multiplex = 2 • Thread D terminé multiplex = 3 • Tous les threads y sont passé. • Quand le multiplex > 0, indique le nombre de places libres pour accéder à la zone critique. • Quand le multiplex <0, la valeur négative indique le nombre de « threads » en attente d’un accès à la zone critique.
Barrière • Code: • Indice de solution:
Barrière • Solution: Porte tournante #1 Porte tournante #2
File (d’attente) • Salle de dance en couple: • Meneurs (leaders) / suiveurs (followers). • Pour pouvoir aller sur la piste de dance, un leader doit accompagner un follower. • Il y aura donc des files d’attentes. • Sémaphores.
File (d’attente) • Pour le meneur: • Pour le suiveur:
File (d’attente) • Pour le meneur: • Pour le suiveur: • L’attente sera-t-elle raisonnable ?
File exclusive • Initialisation: • Accès exclusif à la piste de dance !
File exclusive • Le meneur:
File exclusive • Le suiveur:
Producteur/Consommateur • Problème du producteur/consommateur. • Division du travail entre plusieurs « threads ». • Exemple (programmation événementielle): • Un événement se produit lorsque l’usager appui sur une touche ou bouge la souris. • Un « thread » producteur créée un événement et le met dans une file. • Concurremment les « threads » consommateurs retirent les événements de la file et les traitent.
Producteur/Consommateur • Problème du producteur/consommateur.
Producteur/Consommateur • Initialisation:
Producteur/Consommateur • Producteur: • Consommateur:
Producteur/Consommateur • Cas avec file d’attente de longueur finie:
Producteur/Consommateur • Exemple en Java:
Producteur/Consommateur • Exemple en Java:
Producteur/Consommateur • Exemple en Java:
Producteur/Consommateur • Exemple en Java:
Producteur/Consommateur • Exécution, mais il y a un bogue.