{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Programmation concurente - CORRECTION" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**Ressources** : " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Sommaire\n", "
    \n", "
  1. Introduction\n", "
      \n", "
    1. Les processus
    2. \n", "
    3. Les threads
    4. \n", "
    5. Quelle est la différence entre un Thread et un Processus ?
    6. \n", "
    \n", "
  2. \n", "
  3. Illustration de l'ordre d'exécution de threads
  4. \n", "
  5. Ressource partagée
  6. \n", "
      \n", "
    1. Problème de concurrence
    2. \n", "
    3. Section critique
    4. \n", "
    \n", "
  7. Interblocage
  8. \n", "
  9. Synthèse
  10. \n", "
  11. A savoir
  12. \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Introduction\n", "#### 1.a Les processus\n", "Grâce à leur système d'exploitation **multitâche**, les ordinateurs exécutent plusieurs programmes de façon **concurrente**. L'exécution d'un programme s'appelle un **processus**. C'est le système d'exploitation, et en particulier l'**ordonnanceur** (une des fonctionnalités du **noyau**), qui détermine quel processus s'exécute à un instant donné. Le fait pour un processus d'être interrompu s'appelle une **commutation de contexte**. Plusieurs processus s'exécutant de façon concurrente peuvent s'**interbloquer** s'ils attendent de pouvoir accéder à un même ensemble de **ressources en accès exclusif**.
\n", "
\n", "#### 1.b Les threads\n", "Les **threads** ou processus légers sont des \"sous-processus\", démarrés par un processus et s'exécutant de manière concurrente avec le reste du programme. L'accès à des ressources par plusieurs threads peut être protégé par des **verrous**. Une portion de code comprise entre l'acquisition et le relâchement d'un verrou s'appelle une **section critique**.
\n", "Le module threading de la bibliothèque standard Python permet de démarrer des threads.

\n", "#### 1.c Quelle est la différence entre un Thread et un Processus ?\n", "Les threads (du même processus) s'exécutent dans un espace mémoire partagé, tandis que les processus s'exécutent dans des espaces mémoire différents.\n", " " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Objectifs\n", "> **Illustrer l'ordre d'exécution de threads, les problèmes de concurrence et d'interblocage**. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Illustration de l'ordre d'exécution de threads\n", "Dans le code ci-dessous, le programme principal crée quatre threads th à l'aide de l'instruction threading.Thread(target=hello, arg=[n]). Lorsque l'on crée un thread, on lui transmet une fonction et la liste des arguments de cette fonction. La méthode **start()** lance l'exécution du thread en tâche de fond. Cette méthode rend la main et le programme principal continue de s'exécuter de façon concurrente au(x) thread(s) démarré(s). Une fois la boucle for exécutée, le programme comporte cinq *threads* : les quatre démarrés par start() plus celui associé au programme principal. Un compteur cmpt est créé dans chaque thread pour illustrer leur ordre d'exécution.\n", "\n", "Note : la bibliothèque logging est dédiée à la journalisation." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "06:38:53: PPrinc : avant de lancer le Thread 0\n", "06:38:53: Thread 0: démarrage\n", "06:38:53: PPrinc : avant de lancer le Thread 1\n", "06:38:53: Thread 0 : cmpt0=0\n", "06:38:53: Thread 1: démarrage\n", "06:38:53: PPrinc : avant de lancer le Thread 2\n", "06:38:53: Thread 1 : cmpt1=0\n", "06:38:53: Thread 2: démarrage\n", "06:38:53: PPrinc : avant de lancer le Thread 3\n", "06:38:53: Thread 2 : cmpt2=0\n", "06:38:53: Thread 3: démarrage\n", "06:38:53: Thread 3 : cmpt3=0\n", "06:38:53: Thread 2 : cmpt2=1\n", "06:38:53: Thread 1 : cmpt1=1\n", "06:38:53: Thread 0 : cmpt0=1\n", "06:38:53: Thread 3 : cmpt3=1\n", "06:38:54: Thread 0 : cmpt0=2\n", "06:38:54: Thread 1 : cmpt1=2\n", "06:38:54: Thread 2 : cmpt2=2\n", "06:38:54: Thread 3 : cmpt3=2\n", "06:38:54: Thread 2 : cmpt2=3\n", "06:38:54: Thread 1 : cmpt1=3\n", "06:38:54: Thread 0 : cmpt0=3\n", "06:38:54: Thread 3 : cmpt3=3\n", "06:38:55: Thread 0 : cmpt0=4\n", "06:38:55: Thread 1 : cmpt1=4\n", "06:38:55: Thread 2 : cmpt2=4\n", "06:38:55: Thread 3 : cmpt3=4\n", "06:38:55: Thread 0: terminé\n", "06:38:55: Thread 1: terminé\n", "06:38:55: Thread 2: terminé\n", "06:38:55: Thread 3: terminé\n" ] } ], "source": [ "# Programmation concurente - Illustration de l'ordre d'exécution de threads \n", "import threading\n", "import logging # Cette bibliothèque est dédiée à la journalisation\n", "import time\n", "\n", "# Fonction associée aux threads 0 à 3\n", "def hello(num):\n", " logging.info(f\"Thread {num}: démarrage\")\n", " for i in range(5):\n", " logging.info(f\"Thread {num} : cmpt{num}={i}\")\n", " time.sleep(0.5) # Simulation d'un programme plus long\n", " logging.info(f\"Thread {num}: terminé\")\n", "\n", "# Programme principal\n", "# Formatage des informations affichées lors du déroulement du programme\n", "format = \"%(asctime)s: %(message)s\"\n", "logging.basicConfig(format=format, level=logging.INFO, # filename='thread.log', filemode='a',\n", " datefmt=\"%H:%M:%S\", encoding='utf-8')\n", "for numth in range(4): # Création des threads 0 à 3\n", " th = threading.Thread(target=hello, args=[numth]) # l'argument de type target est une fonction et l'argument \n", " # args est un tableau d'arguments passés à la fonction.\n", " # Ici, on passe le numéro numth du thread th à des fins d'affichage.\n", " logging.info(f\"PPrinc : avant de lancer le Thread {numth}\")\n", " th.start() " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Activité 1
\n", "> Exécutez** plusieurs fois le code ci-dessus. Que peut-on dire de l'ordre d'exécution des threads et de l'ordre dans lequel ils s'arrêtent ?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

CORRECTION Activité 1

\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
REMARQUE : l'ordre dans lequel sont démarrés les threads ne donne aucune indication sur l'ordre dans lequel ils peuvent se terminer.
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. Ressource partagée\n", "#### 3.A Problème de concurence\n", "Dans le programme ci-dessous, la variable globale **COMPTEUR** représente une **ressource partagée** par plusieurs threads. Comme hello dans le programme précédent, la fonction **incrc** s'exécute dans des threads." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "06:35:52: Thread 0 - cpt=0\n", "06:35:52: Thread 0 - cpt=1\n", "06:35:52: Thread 1 - cpt=1\n", "06:35:52: Thread 0 - cpt=2\n", "06:35:52: Thread 2 - cpt=2\n", "06:35:52: Thread 1 - cpt=2\n", "06:35:52: Thread 1 - cpt=3\n", "06:35:52: Thread 1 - cpt=4\n", "06:35:52: Thread 1 - cpt=5\n", "06:35:52: Thread 1 - cpt=6\n", "06:35:52: Thread 1 - cpt=7\n", "06:35:52: Thread 1 - cpt=8\n", "06:35:52: Thread 1 - cpt=9\n", "06:35:52: Thread 1 - cpt=10\n", "06:35:52: Thread 2 - cpt=3\n", "06:35:52: Thread 2 - cpt=4\n", "06:35:52: Thread 0 - cpt=3\n", "06:35:52: Thread 0 - cpt=4\n", "06:35:52: Thread 0 - cpt=5\n", "06:35:52: Thread 3 - cpt=2\n", "06:35:52: Thread 2 - cpt=5\n", "06:35:52: Thread 2 - cpt=6\n", "06:35:52: Thread 3 - cpt=3\n", "06:35:52: Thread 0 - cpt=6\n", "06:35:52: Thread 0 - cpt=7\n", "06:35:52: Thread 3 - cpt=4\n", "06:35:52: Thread 3 - cpt=5\n", "06:35:52: Thread 3 - cpt=6\n", "06:35:52: Thread 2 - cpt=7\n", "06:35:52: Thread 0 - cpt=8\n", "06:35:52: Thread 0 - cpt=9\n", "06:35:52: Thread 2 - cpt=8\n", "06:35:52: Thread 2 - cpt=9\n", "06:35:52: Thread 2 - cpt=10\n", "06:35:52: Thread 2 - cpt=11\n", "06:35:52: Thread 3 - cpt=7\n", "06:35:52: Thread 3 - cpt=8\n", "06:35:52: Thread 3 - cpt=9\n", "06:35:52: Thread 3 - cpt=10\n", "06:35:52: Thread 3 - cpt=11\n", "06:35:52: Valeur finale = 12\n" ] } ], "source": [ "# Programmation concurente - Compteur partagé\n", "# Illustration du problème de concurrence v1\n", "import threading\n", "import logging\n", "import time\n", "\n", "COMPTEUR = 0 # Ressource partagée\n", "\n", "# Fonction associée aux threads 0 à 3\n", "def incrc(n):\n", " global COMPTEUR\n", " for _ in range(10):\n", " v = COMPTEUR\n", " logging.info(f\"Thread {n} - cpt={COMPTEUR}\")\n", " COMPTEUR = v + 1\n", "\n", "# Programme principal\n", "format = \"%(asctime)s: %(message)s\"\n", "logging.basicConfig(format=format, level=logging.INFO,\n", " datefmt=\"%H:%M:%S\", encoding='utf-8')\n", "th=[] # tableau de threads\n", "for n in range(4):\n", " t = threading.Thread(target=incrc, args=[n])\n", " t.start()\n", " th.append(t)\n", "\n", "for t in th: # Permet d'attendre que tous les threads soient terminés avant de poursuivre\n", " t.join() # dans le programme principal\n", "\n", "logging.info(f\"Valeur finale = {COMPTEUR}\") # Cette ligne est exécutée lorsque tous les threads sont terminés" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Activité 2. Analyse et tests** du programme ci-dessus.
\n", "> 1. Que fait la fonction incrc ? \n", "> 2. Quelle doit être la valeur de COMPTEUR à la fin du programme ? \n", "> 3. Testez le programme plusieurs fois. La valeur est-elle toujours celle supposée ? Pourquoi ?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

CORRECTION Activité 2

\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### 3.B Section critique\n", "Pour corriger le problème identifié dans le code précédent, il faut rendre EXCLUSIF l'accès à la variable COMPTEUR. On peut pour cela utiliser un verrou. Un verrou est un objet que l'on essaye d'acquérir. Si un thread est le premier à en faire la demande, il l'acquiert. Il peut le rendre à tout moment. Si en revanche un autre thread le détient alors tous les threads qui tentent d'y accéder sont bloqués jusqu'à ce qu'il soit libéré. On construit un verrou avec la méthode Lock() du module threading. On peut alors tenter de l'acquérir avec la méthode acquire() et le rendre avec la méthode release()." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
NOTE : Une portion de code protégée par un verrou s'appelle une SECTION CRITIQUE.
" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "06:49:00: Thread 0 - cpt=0\n", "06:49:00: Thread 0 - cpt=1\n", "06:49:00: Thread 0 - cpt=2\n", "06:49:00: Thread 0 - cpt=3\n", "06:49:00: Thread 0 - cpt=4\n", "06:49:00: Thread 0 - cpt=5\n", "06:49:00: Thread 0 - cpt=6\n", "06:49:00: Thread 0 - cpt=7\n", "06:49:00: Thread 0 - cpt=8\n", "06:49:00: Thread 0 - cpt=9\n", "06:49:00: Thread 1 - cpt=10\n", "06:49:00: Thread 1 - cpt=11\n", "06:49:00: Thread 1 - cpt=12\n", "06:49:00: Thread 1 - cpt=13\n", "06:49:00: Thread 1 - cpt=14\n", "06:49:00: Thread 1 - cpt=15\n", "06:49:00: Thread 1 - cpt=16\n", "06:49:00: Thread 1 - cpt=17\n", "06:49:00: Thread 1 - cpt=18\n", "06:49:00: Thread 1 - cpt=19\n", "06:49:00: Thread 3 - cpt=20\n", "06:49:00: Thread 3 - cpt=21\n", "06:49:00: Thread 3 - cpt=22\n", "06:49:00: Thread 3 - cpt=23\n", "06:49:00: Thread 3 - cpt=24\n", "06:49:00: Thread 3 - cpt=25\n", "06:49:00: Thread 3 - cpt=26\n", "06:49:00: Thread 3 - cpt=27\n", "06:49:00: Thread 3 - cpt=28\n", "06:49:00: Thread 3 - cpt=29\n", "06:49:00: Thread 2 - cpt=30\n", "06:49:00: Thread 2 - cpt=31\n", "06:49:00: Thread 2 - cpt=32\n", "06:49:00: Thread 2 - cpt=33\n", "06:49:00: Thread 2 - cpt=34\n", "06:49:00: Thread 2 - cpt=35\n", "06:49:00: Thread 2 - cpt=36\n", "06:49:00: Thread 2 - cpt=37\n", "06:49:00: Thread 2 - cpt=38\n", "06:49:00: Thread 2 - cpt=39\n", "06:49:00: Valeur finale = 40\n" ] } ], "source": [ "# Programmation concurente - Compteur partagé\n", "# Illustration du problème de concurrence v1\n", "import threading\n", "import logging\n", "import time\n", "\n", "COMPTEUR = 0 # Ressource partagée\n", "verrou = threading.Lock() # construction du verrou\n", "\n", "# Fonction associée aux threads 0 à 3\n", "def incrc(n):\n", " global COMPTEUR\n", " for _ in range(10):\n", " verrou.acquire() # Acquisition du verrou\n", " v = COMPTEUR\n", " logging.info(f\"Thread {n} - cpt={COMPTEUR}\")\n", " COMPTEUR = v + 1\n", " verrou.release() # Relâchement du verrou\n", "\n", "# Programme principal\n", "format = \"%(asctime)s: %(message)s\"\n", "logging.basicConfig(format=format, level=logging.INFO,\n", " datefmt=\"%H:%M:%S\", encoding='utf-8')\n", "th=[] # tableau de threads\n", "for n in range(4):\n", " t = threading.Thread(target=incrc, args=[n])\n", " t.start()\n", " th.append(t)\n", "\n", "for t in th: # Permet d'attendre que tous les threads soient terminés avant de poursuivre\n", " t.join() # dans le programme principal\n", "\n", "logging.info(f\"Valeur finale = {COMPTEUR}\") # Cette ligne est exécutée lorsque tous les threads sont terminés" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Activité 3**
\n", "> Un verrou est créé dans le programme ci-dessus par : verrou = threading.Lock()
\n", "> L'objet verrou possède deux méthodes : **acquire()** et **release()**
\n", ">
\n", "> a) Placez le verrou dans le code ci-dessus pour protéger la section critique.
\n", "> b) Testez le programme avec différentes bornes pour la boucle for. Que remarquez-vous ?
\n", "> c) Expliquez pourquoi on a corrigé le problème de concurrence entre les threads t0, t1, t2 et t3." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

CORRECTION Activité 3

\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. Interblocage\n", "L'interblocage se produit lorsque des processus concurrents **s'attendent mutuellement**. L'utilisation de plusieurs verrous rend le risque d'**interblocages** possible.
\n", "Dans l'exemple ci-dessous **P1** et **P2** sont **interbloqués** car :
\n", "- Le processus **P1** dispose de la ressource **D1** et attend la ressource **D2**.
\n", "- Le processus **P2** dispose de la ressource **D2** et attend la ressource **D1**.
\n", "" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Illustration de linterblocage\n", "# La fonction P1 essaye d'acquérir d'abord verrou1 puis verrou2, alors que P2 essaye de \n", "# les acquérir dans l'ordre inverse.\n", "# Si on exécute ce programme, il a de grandes chances de se retrouver bloqué.\n", "import threading\n", "import logging\n", "\n", "verrou1 = threading.Lock()\n", "verrou2 = threading.Lock()\n", "\n", "def p1():\n", " verrou1.acquire()\n", " logging.info(\"P1 a acquit D1\") \n", " verrou2.acquire()\n", " logging.info(\"P1 a acquit D2\")\n", " verrou2.release()\n", " logging.info(\"P1 a rendu D2\")\n", " verrou1.release()\n", " logging.info(\"P1 a rendu D1\")\n", " \n", " \n", "def p2():\n", " verrou2.acquire()\n", " logging.info(\"P2 a acquit D2\") \n", " verrou1.acquire()\n", " logging.info(\"P2 a acquit D1\")\n", " verrou1.release()\n", " logging.info(\"P2 a rendu D1\")\n", " verrou2.release()\n", " logging.info(\"P2 a rendu D2\")\n", "\n", "# Programme principal\n", "format = \"%(asctime)s: %(message)s\"\n", "logging.basicConfig(format=format, level=logging.INFO,\n", " datefmt=\"%H:%M:%S\", encoding='utf-8')\n", "\n", "t1 = threading.Thread(target=p1, args=[])\n", "t2 = threading.Thread(target=p2, args=[])\n", "t1.start()\n", "t2.start()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Activité 4a. Analyse** du programme ci-dessus
\n", "Quel pourrait être le texte affiché par le programme :
\n", "a) S'il ne se bloque pas ?
\n", "b) S'il se bloque ? " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

CORRECTION 4a

\n", "

a) Pas d'interblocage (exemple)

\n", "\n", "

b) Interblocage (1 seule solution)

\n", "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Activité 4b**
\n", ">**Supprimer** l'interblocage dans le programme ci-dessous." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Correction de linterblocage\n", "import threading\n", "import logging\n", "import time\n", "\n", "verrou1 = threading.Lock()\n", "verrou2 = threading.Lock()\n", "\n", "def p1():\n", " verrou1.acquire()\n", " logging.info(\"P1 a acquit D1\") \n", " verrou2.acquire()\n", " logging.info(\"P1 a acquit D2\")\n", " verrou2.release()\n", " logging.info(\"P1 a rendu D2\")\n", " verrou1.release()\n", " logging.info(\"P1 a rendu D1\")\n", " \n", " \n", "def p2():\n", " verrou1.acquire()\n", " logging.info(\"P2 a acquit D1\") \n", " verrou2.acquire()\n", " logging.info(\"P2 a acquit D2\")\n", " verrou2.release()\n", " logging.info(\"P2 a rendu D2\")\n", " verrou1.release()\n", " logging.info(\"P2 a rendu D1\")\n", "\n", "# Programme principal\n", "format = \"%(asctime)s: %(message)s\"\n", "logging.basicConfig(format=format, level=logging.INFO,\n", " datefmt=\"%H:%M:%S\", encoding='utf-8')\n", "\n", "t1 = threading.Thread(target=p1, args=[])\n", "t2 = threading.Thread(target=p2, args=[])\n", "t1.start()\n", "t2.start()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 5. SYNTHESE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Activité 5**
\n", "> En vous inspirant du programme du paragraphe 2, **écrivez** un programme qui crée et démarre **quatre** fonctions concurrentes affichant plusieurs fois un message de bienvenue personnalisé (maximum **dix** fois le message).
\n", "\n", "> *Exemple de résultats attendus*
\n", "Bonjour, je suis le thread 0 et ceci est le message 1
\n", "...
\n", "Message de bienvenue du thread 1 qui transmet son message 3
\n", "...
" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Correction Activité 5\n", "# Programmation concurente - Messages différents dans chaque thread\n", "\n", "import threading\n", "import logging\n", "import time\n", "\n", "'''\n", "numth : numéro du thread\n", "msg : tableau des messages à afficher\n", "nb : tableau des nombres de messages\n", "'''\n", "\n", "# Fonction associée aux threads 0 à 3\n", "def hello(num,msg,nb):\n", " for i in range(nb):\n", " logging.info(f\"{msg} {i}\")\n", " logging.info(f\"Thread {num}: terminé\")\n", "\n", "# Programme principal\n", "format = \"%(asctime)s: %(message)s\"\n", "logging.basicConfig(format=format, level=logging.INFO,\n", " datefmt=\"%H:%M:%S\", encoding='utf-8')\n", "nb=[10,7,5,8]\n", "msg=[\"Bonjour, je suis le thread 0 et ceci est le message \",\"Message de bienvenue du thread 1 qui transmet le message \",\n", " \"Salut, le thread 2 vous envoie le message \", \"Hé, le thread 3 aussi envoie son message \"]\n", "\n", "for num in range(len(nb)):\n", " t = threading.Thread(target=hello, args=[num,msg[num], nb[num]]) \n", " t.start()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Activité 6a**
\n", "> On considère un petit système embarqué : un **microcontrôleur** relié à trois **LED A, B, C**. Une LED peut être éteinte ou éclairée et on peut configurer sa couleur. On dispose de trois programmes qui affichent des signaux lumineux en faisant clignoter les LED. Chaque programme possède une LED primaire et une LED secondaire.
\n", "> - Le programme P1 émet ses signaux sur A (primaire) et B (secondaire) en vert.
\n", "> - Le programme P2 émet ses signaux sur B (primaire) et C (secondaire) en bleu.
\n", "> - Le programme P3 émet ses signaux sur C (primaire) et A (secondaire) en rouge.
\n", ">\n", "> Comme les LED ne peuvent pas être configurées dans deux couleurs en même temps, le système propose deux primitives **acquerirLED(nom)** et **rendreLED(nom)** qui permettent respectivement d'acquérir et de relâcher une LED.
**nom** prend la valeur primaire ou secondaire.
Si une LED est déjà acquise par un programme Pi alors acquerirLED(nom) dans un programme Pj bloque Pj (i différent de j).
\n", "> On suppose que chacun des trois programmes P1, P2, P3 effectue les **actions** suivantes en boucle :
\n", "> 1. acquérir la LED primaire\n", "> 2. acquérir la LED secondaire\n", "> 3. configurer les couleurs\n", "> 4. émettre des signaux\n", "> 5. rendre la LED secondaire \n", "> 6. rendre la LED primaire puis\n", "> recommencer en 1\n", ">\n", "> **Montrer** qu'il existe un entrelacement des exécutions qui place **P1, P2 et P3 en interblocage**." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

CORRECTION Activié 6a

\n", "

Le contexte peut être schématisé comme ci-dessous.

\n", "
\n", " On considère l'enchaînement suivant :
\n", "\n", "\n", "On obtient la boucle A -> P1 -> B -> P2 -> C -> P3 -> A
\n", "A ce stade, les trois processus sont bloqués dans une attente circulaire d'une ressource tenue par un autre processus. Ils sont en interblocage. \n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> **Activité 6b**
\n", "Téléchargez, copiez et complétez le code situé ici : https://gist.github.com/WebGE/f24c17bb13f10b38eaf21725451e3754\n", ">\n", "> *Exemple de résultats attendus*
\n", "11:21:14: LedA=vert par P1
\n", "11:21:14: LedB=vert par P1
\n", "11:21:14: LedC=rouge par P3
\n", "11:21:14: LedB relachée par P1
\n", "11:21:14: LedB=bleu par P2
\n", "11:21:14: LedA relachée par P1
\n", "11:21:14: LedA=rouge par P3
\n", "11:21:14: LedA relachée par P3
\n", "11:21:14: LedA=vert par P1
\n", "11:21:14: LedC relachée par P3
\n", "11:21:14: LedC=bleu par P2
\n", "11:21:14: LedC relachée par P2
" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "07:09:22: LedA=vert par P1\n", "07:09:22: LedB=vert par P1\n", "07:09:22: LedB relachée par P1\n", "07:09:22: LedB=bleu par P2\n", "07:09:22: LedA relachée par P1\n", "07:09:22: LedA=vert par P1\n", "07:09:22: LedC=rouge par P3\n" ] } ], "source": [ "# Correction 6b\n", "# MICROLED - Illustration de l'interblocage dans la commande des Leds\n", "\n", "import threading\n", "import logging\n", "\n", "VERROU_LED={}\n", "VERROU_LED['A']=threading.Lock()\n", "VERROU_LED['B']=threading.Lock()\n", "VERROU_LED['C']=threading.Lock()\n", "\n", "def acquerirLED(led):\n", " VERROU_LED[led].acquire()\n", "\n", "def rendreLED(led):\n", " VERROU_LED[led].release()\n", " \n", "def prog(numproc,ledprim,ledsec,couleur):\n", " while True:\n", " acquerirLED(ledprim)\n", " logging.info(f\"Led{ledprim}={couleur} par P{numproc}\")\n", " acquerirLED(ledsec)\n", " logging.info(f\"Led{ledsec}={couleur} par P{numproc}\")\n", " rendreLED(ledsec)\n", " logging.info(f\"Led{ledsec} relachée par P{numproc}\")\n", " rendreLED(ledprim)\n", " logging.info(f\"Led{ledprim} relachée par P{numproc}\")\n", "\n", "# Programme principal\n", "format = \"%(asctime)s: %(message)s\"\n", "logging.basicConfig(format=format, level=logging.INFO,\n", " datefmt=\"%H:%M:%S\", encoding='utf-8')\n", "\n", "p1 = threading.Thread(target=prog, args=[1,'A','B','vert'])\n", "p2 = threading.Thread(target=prog, args=[2,'B','C','bleu'])\n", "p3 = threading.Thread(target=prog, args=[3,'C','A','rouge'])\n", "\n", "p1.start();p2.start();p3.start();" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 6. A retenir\n", "Les systèmes d'exploitation multitâches sont la norme. Ils permettent d'exécuter de façon concurrente plusieurs programmes. L'exécution d'un programme s'appelle un processus. C'est le système d'exploitation et en particulier l'ordonnanceur, qui détermine quel processus s'exécute à un instant donné. Le fait pour un processus d'être interrompu s'appelle une commutation de contexte. Plusieurs processus s'exécutant de façon concurrente peuvent s'interbloquer s'ils attendent de pouvoir accéder à un même ensemble de ressources en accès exclusif. Les threads ou processus légers sont des \"sous-processus\" s'exécutant de manière concurrente. L'accès à des ressources par plusieurs threads peut être protégé par des verrous. Une portion de code comprise entre l'acquisition et le relâchement d'un verrou s'appelle une section critique. Numérique et Sciences Informatiques - ellipses" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.5" } }, "nbformat": 4, "nbformat_minor": 4 }