Differences

This shows you the differences between two versions of the page.

Link to this comparison view

sde:laboratoare:09_python [2020/04/14 20:05]
diana.ghindaoanu [Occupation / libération d'un mutex]
— (current)
Line 1: Line 1:
-====== TP 9 - Synchronisation des threads ====== 
- 
-===== Présentation théorique ===== 
- 
-Pour la synchronisation des fils d'​exécution,​ on dispose de: 
-    
-   * [[#mutex | mutex]] 
-   * [[#​sémaphore | sémaphores]] 
-   * [[#​variables de condition | variables de condition]] 
-   * [[#​barrière | barrières]] 
- 
-===== Mutex ===== 
- 
-Les mutex (verrous d'​exclusion mutuelle) sont des objets de synchronisation utilisés pour garantir ** un accès exclusif ** dans une section de code où ** les données partagées ** sont utilisées entre deux threads ou plusieurs. Un mutex a deux états possibles: ** occupé ** et ** libre **. Un mutex peut être occupé par un ** seul thread ** à la fois. Lorsqu'​un mutex est occupé par un thread, il ne peut être occupé par aucun autre thread. Dans ce cas, une demande d'​emploi d'un autre thread bloquera généralement le thread jusqu'​à ce que le mutex devienne libre. 
- 
-==== Création d'un mutex ==== 
- 
-En Python, le mutex est représenté par la classe [[https://​docs.python.org/​3/​library/​threading.html#​threading.Lock|threading.Lock]]. Lors de la création du mutex, il est à l'​état ** déverrouillé **. Une fois occupé par un thread, tout autre appel à occuper le mutex échouera. 
- 
-=== Mutex récursif === 
- 
-En utilisant la classe [[https://​docs.python.org/​3/​library/​threading.html#​threading.RLock|threading.RLock]],​ nous pouvons créer un mutex récursif. Ce mutex peut être occupé plusieurs fois par le même thread. Pour être libéré pour un autre thread, le thread qui a occupé le mutex doit appeler la fonction de libération un certain nombre de fois egal avec le nombre d'​appels de la fonction d'​occupation du mutex. 
- 
-==== Occupation / libération d'un mutex ==== 
- 
-Les fonctions d'​occupation / de libération d'un mutex ([[https://​docs.python.org/​3/​library/​threading.html#​threading.Lock.acquire|acquire]], ​ 
-[[https://​docs.python.org/​3/​library/​threading.html#​threading.Lock.release|release]]):​ 
- 
-<code python> 
-mutex.acquire (blocking=True,​ timeout=-1) 
-mutex.release() 
-</​code>​ 
- 
-Si le mutex est ** libre ** au moment de l'​appel,​ il sera occupé par le fil appelant et la fonction retournera immédiatement. Si le mutex est occupé par un ** autre thread **, le comportement de la fonction dépend du paramètre ** blockling **: 
-   * ''​ True ''​ - le thread se verrouille jusqu'​à ce que le mutex soit libéré; si le paramètre ** timeout ** a une valeur positive, le thread ne se verrouillera que pendant ** timeout ** secondes (dans ce cas, la fonction renverra ** True ** si le mutex a été occupé et ** False * * sinon); 
-   * ''​ Faux ''​ - la fonction renvoie immédiatement la valeur ** Vrai ** si la fonction ** acquérir ** a fonctionné et la valeur ** Faux ** si l'​appel a échoué (le mutex est occupé par un autre thread). 
- 
-Une commande FIFO pour l'​emploi d'un mutex n'est pas garantie. ** N'​importe lequel des fils ** en attente de déverrouillage d'un mutex peut l'​attraper. 
- 
-Pour libérer un mutex, vous devez appeler la fonction ** release **. 
- 
-<note info> 
-Si la fonction ** release ** est appelée sur un mutex ** free **, la fonction générera une exception. 
-</​note>​ 
- 
-==== Exemple d'​utilisation de mutex ==== 
- 
-Un exemple d'​utilisation d'un mutex pour sérialiser l'​accès à la variable globale ''​ global_counter '':​ 
- 
-<code python> 
-import threading 
-  
-NUM_THREADS = 5 
-  
-# global mutex 
-mutex = threading.Lock() 
- 
-global_counter = 0 
-def thread_routine(): ​ 
-    global global_counter 
-    ​ 
-    thread_id = threading.get_ident() 
-    # acquire global mutex 
-    mutex.acquire() 
-  
-    # print and modify global_counter 
-    print("​Thread {} says global_counter={}"​.format (thread_id, global_counter)) 
-    global_counter = global_counter + 1 
-  
-    # release mutex - now other threads can modify global_counter 
-    mutex.release() 
- 
-threads = [] 
-# all threads execute thread_routine 
-for i in range (NUM_THREADS):​ 
-    t = threading.Thread (target=thread_routine) 
-    threads.append (t) 
-    t.start() 
- 
-for i in range (NUM_THREADS):​ 
-    threads[i].join() 
-</​code>​ 
- 
-<code bash> 
-$ python3 example.py ​ 
-Thread 123145557700608 says global_counter=0 
-Thread 123145562955776 says global_counter=1 
-Thread 123145557700608 says global_counter=2 
-Thread 123145568210944 says global_counter=3 
-Thread 123145562955776 says global_counter=4 
-</​code>​ 
- 
-===== Sémaphores ===== 
- 
-Les sémaphores sont des objets de synchronisation qui représentent une généralisation des mutex dans la mesure où ils ** enregistrent le nombre d'​opérations de libération ** (incrément) effectuées sur eux. Fondamentalement,​ un sémaphore est un entier qui augmente / diminue atomiquement. La valeur d'un sémaphore ne peut pas devenir négative. Si le sémaphore a une valeur de 0, l'​opération de décrémentation sera bloquée jusqu'​à ce que la valeur du sémaphore devienne strictement positive. Les mutex peuvent donc être considérés comme des sémaphores binaires. 
- 
-En Python, les semaphores sont représentés par la classe [[https://​docs.python.org/​3/​library/​threading.html#​threading.Semaphore|Semaphore]]:​ 
- 
-<code python> 
-import threading 
-semaphore = threading.Semaphore (value=1) 
-</​code>​ 
- 
-==== Opérations ==== 
- 
-   * Occupation du sémaphore 
-([[https://​docs.python.org/​3/​library/​threading.html#​threading.Semaphore.acquire|acquire]]) - diminue le nombre du sémaphore; 
-   * libération du sémaphore ([[https://​docs.python.org/​3/​library/​threading.html#​threading.Semaphore.release|release]] - augmente le compteur du sémaphore. 
- 
-La fonction [[https://​docs.python.org/​3/​library/​threading.html#​threading.Semaphore.acquire|acquire]] est similaire à la fonction ** acquire** spécifique au mutex. La différence est que dans ce cas, à l'​appel ** acquire ** il est vérifié si le compteur du sémaphore est supérieur à 0. 
- 
-==== Sémaphore limité ==== 
- 
-En plus de la classe ** Semaphore **, le ** module de threading ** exporte la classe [[https://​docs.python.org/​3/​library/​threading.html#​threading.BoundedSemaphore|BoundedSemaphore]] qui garantit que la fonction ** release ** n'est pas appelé plus que la fonction **acquire**. Si l'​appel de la fonction ** release ** entraîne une augmentation du compteur supérieure à la valeur initiale, l'​appel de la fonction entraînera une erreur. 
- 
- 
-==== Exemple d'​utilisation du sémaphore ==== 
-<code python> 
-import threading 
-  
-NUM_THREADS = 5 
-  
-# global semaphore 
-my_sem = threading.Semaphore(value=2) 
- 
-add_counter = 0 
-subtract_counter = 0 
- 
-def add_routine(): ​ 
-    global add_counter 
-    ​ 
-    thread_id = threading.get_ident() 
-    # acquire global mutex 
-    my_sem.acquire() 
-  
-    # print and modify global_counter 
-    print("​Thread {} says add_counter={}"​.format (thread_id, add_counter)) 
-    add_counter = add_counter + 1 
-  
-    # release mutex - now other threads can modify global_counter 
-    my_sem.release() 
- 
-def subtract_routine(): ​ 
-    global subtract_counter 
-    ​ 
-    thread_id = threading.get_ident() 
-    # acquire global mutex 
-    my_sem.acquire() 
-  
-    # print and modify global_counter 
-    print("​Thread {} says subtract_counter={}"​.format (thread_id, subtract_counter)) 
-    subtract_counter = subtract_counter - 1 
-  
-    # release mutex - now other threads can modify global_counter 
-    my_sem.release() 
- 
-threads = [] 
-# all threads execute thread_routine 
-for i in range (NUM_THREADS):​ 
-    t1 = threading.Thread (target=add_routine) 
-    t2 = threading.Thread (target=subtract_routine) 
-    threads.append (t1) 
-    t1.start() 
-    threads.append (t2) 
-    t2.start() 
- 
-for i in range (NUM_THREADS):​ 
-    threads[i].join() 
-</​code>​ 
- 
-<code bash> 
-$ python3 example.py ​ 
-Thread 123145375227904 says add_counter=0 
-Thread 123145380483072 says subtract_counter=0 
-Thread 123145375227904 says add_counter=1 
-Thread 123145375227904 says subtract_counter=-1 
-Thread 123145375227904 says add_counter=2 
-Thread 123145380483072 says subtract_counter=-2 
-Thread 123145375227904 says subtract_counter=-2 
-Thread 123145385738240 says add_counter=3 
-Thread 123145380483072 says add_counter=4 
-Thread 123145375227904 says subtract_counter=-4 
-</​code>​ 
- 
-<note info> 
-Nous pouvons voir que les deux threads (add et subtract) fonctionnent en parallèle. 
-</​note>​ 
- 
-===== Variables de condition ===== 
- 
-Les variables de condition fournissent un système de notification pour les threads d'​exécution,​ permettant à un thread de se bloquer en attendant le signal d'un autre thread. L'​utilisation correcte des variables de condition nécessite un protocole coopératif entre les threads d'​exécution. 
- 
-Les mutex et les feux de signalisation permettent de verrouiller ** d'​autres threads ** d'​exécution. Les variables de condition sont utilisées pour bloquer le ** fil de courant ** jusqu'​à ce qu'une condition soit remplie. 
- 
-Les variables de condition sont des objets de synchronisation qui permettent à un thread de suspendre son exécution jusqu'​à ce qu'une condition (prédicat logique) ** devienne vraie **. Lorsqu'​un thread d'​exécution détermine que le prédicat est devenu vrai, il signale la condition de variable, déverrouillant ainsi un ou tous les threads d'​exécution verrouillés sur cette condition de variable (selon l'​intention). 
- 
-En Python, une variable de condition est la classe [[https://​docs.python.org/​3/​library/​threading.html#​threading.Condition|Condition]]. 
- 
-Une condition variable doit toujours être utilisée ** avec un mutex ** pour éviter la course qui se produit lorsqu'​un thread se prépare à attendre la condition variable après l'​évaluation du prédicat logique, et un autre thread signale la condition variable avant même le premier thread il se verrouille, perdant ainsi le signal. 
- 
-Lors de la création de la condition, nous pouvons passer au constructeur comme paramètre un mutex ou nous pouvons laisser le constructeur en créer un. 
- 
-<code python> 
-import threading 
- 
-mutex = threading.Lock() 
-mutex_cond = threading.Condition (mutex) 
-default_mutex_cond = threading.Condition() 
-</​code>​ 
- 
-Par conséquent,​ les opérations de signalisation,​ de test de la condition logique et de blocage de la variable de condition doivent être effectuées en ayant ** occupé ** le mutex associé à la variable de condition. La condition logique est testée sous la protection du mutex, et si elle n'est pas remplie, le thread appelant se verrouille sur la condition variable, libérant atomiquement le mutex. Au moment du déverrouillage,​ un thread tentera d'​occuper le mutex associé à la condition variable. De plus, le test du prédicat logique doit être effectué dans une ** boucle **, car si plusieurs threads sont libérés à la fois, un seul pourra occuper le mutex associé à la condition. Le reste attendra qu'il soit libéré, mais il est possible que le thread qui occupait le mutex ** change ** la valeur du prédicat logique pendant la propriété du mutex. Pour cette raison, les autres threads doivent à nouveau tester le prédicat car, sinon, il commencerait son exécution en supposant le vrai prédicat, alors qu'il est en fait faux. 
- 
-<note info> 
-On va utiliser les fonctions **acquire** et **release** sur l'​objet condition. 
-</​note>​ 
- 
-==== Verrouillage sur une condition variable ==== 
- 
-Pour suspendre l'​exécution et attendre une condition variable, un thread appellera l'une des fonctions [[https://​docs.python.org/​3/​library/​threading.html#​threading.Condition.wait|wait ]] ou [[https://​docs.python.org/​3/​library/​threading.html#​threading.Condition.wait_for|wait_for]]:​ 
- 
-<code python> 
-wait(timeout=None) 
-wait_for(predicate,​ timeout=None) 
-</​code>​ 
- 
-La différence entre les deux fonctions est que ** wait ** attend qu'une notification soit émise, tandis que ** wait_for ** attend que le prédicat ait la valeur ** True **. 
- 
-Le thread appelant doit avoir ** déjà occupé ** le mutex associé, au moment de l'​appel. La fonction '​attente'​ va ** libérer ** le mutex et ** verrouiller **, en attendant que la condition variable soit ** signalée ** par un autre thread. Les deux opérations sont effectuées ** atomiquement **. Lorsque la condition variable est signalée, il essaiera d'​occuper le mutex associé, et après ** l'​occuper,​ l'​appel de fonction reviendra. Notez que le thread appelant peut être suspendu, après déblocage, en attendant l'​utilisation du mutex associé, tandis que le prédicat logique, vrai au moment du déblocage du thread, peut être modifié par d'​autres threads. Par conséquent,​ l'​appel d'​attente doit être effectué dans une boucle dans laquelle la valeur de vérité du prédicat logique associé à la condition de variable est testée, pour garantir une sérialisation correcte des threads d'​exécution. Un autre argument pour tester en boucle le prédicat logique est qu'un appel «d'​attente» peut être ** interrompu ** par un signal asynchrone (voir laboratoire de signaux), avant que le prédicat logique ne devienne vrai. Si les threads d'​exécution en attente de la condition de variable ne testaient pas à nouveau le prédicat logique, ils poursuivraient leur exécution en supposant que cela était vrai. 
- 
-==== Déverrouillage d'un seul thread verrouillé dans une variable de condition ==== 
- 
-Pour déverrouiller un seul fil d'​exectution verrouillé sur une variable de condition, la variable de condition sera marquée à l'aide de [[https://​docs.python.org/​3/​library/​threading.html#​threading.Condition.notify|notify]]:​ 
- 
-<code python> 
-notify(n=1) 
-</​code>​ 
- 
-Si aucun thread d'​exécution n'​attend la condition variable, l'​appel de fonction n'a aucun effet et le signal sera ** perdu **. Si plusieurs threads attendent la condition variable, un seul d'​entre eux sera déverrouillé. Le thread qui sera déverrouillé est choisi par le planificateur de threads. On ne peut pas supposer que les fils en attente seront déverrouillés dans l'​ordre dans lequel ils ont commencé à attendre. Le thread appelant doit avoir le ** mutex ** associé à la condition de variable au moment de l'​appel de cette fonction. 
- 
-Exemple: 
- 
-<code python> 
-import threading 
-import time 
- 
-mutex = threading.Lock() 
-cond = threading.Condition(mutex) 
- 
-count = 0 
- 
-def decrement_count():​ 
-    global count 
-    id = threading.get_ident() 
-    cond.acquire() 
-    print ('{} a blocat mutex-ul.'​.format (id)) 
-    while count == 0: 
-        cond.wait() 
-    count = count - 1 
-    print ('​count = {}' .format(count)) 
-    cond.release() 
-    print ('{} a eliberat mutex-ul.'​.format(id)) 
- 
-  
-def increment_count():​ 
-    global count 
-    id = threading.get_ident() 
-    if cond.acquire(blocking=False):​ 
-        print ('​Mutex-ul a fost eliberat'​) 
-        print ('{} a blocat mutex-ul.'​.format(id)) 
-        count = count + 1 
-        cond.notify() 
-        print ('​count = {}' .format(count)) 
-        cond.release() 
-        print ('{} a eliberat mutex-ul.'​.format(id)) 
- 
-t1 = threading.Thread (target=decrement_count) 
-t1.start() 
- 
-time.sleep (2) 
- 
-t2 = threading.Thread (target=increment_count) 
-t2.start() 
-</​code>​ 
- 
-<note info> 
-Nous pouvons voir qu'à l'​appel ** wait **, le mutex est libéré et peut être occupé par un autre thread. 
-</​note>​ 
- 
-==== Déverrouillage de tous les threads échoués dans une condition variable ==== 
- 
-Pour déverrouiller tous les threads verrouillés sur une variable de condition, signalez la variable de condition à l'aide de [[https://​docs.python.org/​3/​library/​threading.html#​threading.Condition.notify_all|notify_all]]:​ 
- 
-<code python> 
-notify_all() 
-</​code>​ 
- 
-Si aucun thread d'​exécution n'​attend la condition variable, l'​appel de fonction n'a aucun effet et le signal sera ** perdu **. Si la variable d'​exécution attend des threads d'​exécution,​ tous ces éléments seront déverrouillés,​ mais ils seront ** en concurrence ** pour l'​occupation mutex associée à la condition de variable. Le thread appelant doit avoir le mutex associé à la condition de variable au moment de l'​appel de cette fonction. 
-==== Exemple d'​utilisation des variables de condition ==== 
- 
-Dans le programme suivant, une barrière est utilisée pour synchroniser les threads d'​exécution du programme. La barrière est implémentée à l'aide d'une variable de condition. 
- 
-<code python> 
-import threading 
-  
-NUM_THREADS = 5 
-nr_still_to_come = NUM_THREADS 
-  
-condition = threading.Condition() 
- 
-def thread_routine():​ 
-    global nr_still_to_come 
-    id = threading.get_ident() 
-  
-    # înainte de a lucra cu datele interne ale barierei trebuie să preluam mutex-ul 
-    condition.acquire() 
-  
-    print("​Thread {}: before the barrier"​.format (id)) 
-  
-    # suntem ultimul fir de execuție care a sosit la barieră? 
-    if nr_still_to_come == 1: 
-        is_last_to_arrive = True 
-    else: 
-        is_last_to_arrive = False 
-    # decrementăm numarul de fire de execuție așteptate la barieră 
-    nr_still_to_come = nr_still_to_come - 1 
-  
-    # cât timp mai sunt fire de execuție care nu au ajuns la barieră, așteptăm. 
-    while nr_still_to_come != 0: 
-        # mutex-ul se eliberează automat înainte de a incepe așteptarea 
-        condition.wait() ​ 
-  
-    # ultimul fir de execuție ajuns la barieră va semnaliza celelalte fire  
-    if is_last_to_arrive:​ 
-        print(" ​   let the flood in") 
-        condition.notify_all() 
-  
-    print("​Thread {}: after the barrier"​. format (id)) 
-  
-    # la ieșirea din funcția de așteptare se preia automat mutex-ul, care trebuie eliberat 
-    condition.release() 
- 
-threads = [] 
- 
-for i in range (NUM_THREADS):​ 
-    t = threading.Thread (target=thread_routine) 
-    threads.append (t) 
-    t.start() 
- 
-for i in range (NUM_THREADS):​ 
-    threads[i].join() 
-</​code>​ 
- 
-<code bash> 
-$ python3 example.py ​ 
-Thread 123145396199424:​ before the barrier 
-Thread 123145401454592:​ before the barrier 
-Thread 123145406709760:​ before the barrier 
-Thread 123145411964928:​ before the barrier 
-Thread 123145417220096:​ before the barrier 
-    let the flood in 
-Thread 123145417220096:​ after the barrier 
-Thread 123145411964928:​ after the barrier 
-Thread 123145396199424:​ after the barrier 
-Thread 123145401454592:​ after the barrier 
-Thread 123145406709760:​ after the barrier 
-</​code>​ 
- 
-Dès l'​exécution du programme, on observe: 
-   * l'​ordre dans lequel les threads d'​exécution sont planifiés ** n'est pas nécessairement l'​ordre de leur création 
-   * l'​ordre dans lequel les threads d'​exécution en attente d'une variable de condition sont réveillés ** n'est pas ** l'​ordre dans lequel ils ont attendu. 
- 
-===== Barrière ===== 
- 
-La norme POSIX définit également un ensemble de fonctions et de structures de données avec des barrières. 
- 
-En Python, la barrière est implémentée par la classe [[https://​docs.python.org/​3/​library/​threading.html#​threading.Barrier|Barrier]]. 
- 
-==== Création d'une barrière ==== 
- 
-Lors de la création d'une barrière, le nombre de threads qui ** attendront ** à la barrière sera spécifié: 
-<code python> 
-barrier = threading.Barrier (parties, action=None,​ timeout=None) 
-</​code>​ 
- 
-L'​action est une fonction qui sera appelée uniquement par le premier thread exécuté ** après ** la barrière. 
- 
-==== En attendant une barrière ==== 
- 
-L'​attente de la barrière se fait en appelant [[https://​docs.python.org/​3/​library/​threading.html#​threading.Barrier.wait|wait]]:​ 
- 
-<code python> 
-barrier.wait() 
-</​code>​ 
- 
-Si la barrière a été créée avec ''​ parties = NUM_THREADS '',​ les premiers threads ''​ NUM_THREADS-1 ''​ qui appellent ''​ attente ''​ sont bloqués. Lorsque le dernier ** ** (du ''​ NUM_THREAD ''​) arrive, il débloquera tous les threads ''​ NUM_THREAD-1 ''​. La fonction '​wait'​ renvoie des valeurs de 0 à NUM_THREADS-1,​ une valeur différente pour chaque thread. 
- 
-==== Exemple d'​utilisation de la barrière ==== 
- 
-Avec des barrières, le programme ci-dessus peut être simplifié: 
- 
-<code python> 
-import threading 
- 
-NUM_THREADS = 5 
- 
-def print_flood():​ 
-    print(" ​  let the flood in"​) ​ 
- 
-def thread_routine():​ 
-    id = threading.get_ident() 
-  
-    print("​Thread {}: before the barrier"​.format(id)) 
-  
-    # toate firele de execuție așteaptă la barieră. 
-    rc = barrier.wait() ​           
-    print("​Thread {}: after the barrier"​.format(id)) 
- 
-# bariera este inițializată o singură dată și folosită de toate firele de execuție 
-# cel de-al doilea parametru este o functie care va fi apelata doar de primul thread care a primit release 
-barrier = threading.Barrier (NUM_THREADS,​ print_flood) ​ 
- 
-threads = [] 
-for i in range (NUM_THREADS):​ 
-    t = threading.Thread (target=thread_routine) 
-    threads.append (t) 
-    t.start() 
-for i in range (NUM_THREADS):​ 
-    threads[i].join() 
-</​code>​ 
- 
-<code bash> 
-$ python3 example.py ​ 
-Thread 123145501237248:​ before the barrier 
-Thread 123145506492416:​ before the barrier 
-Thread 123145511747584:​ before the barrier 
-Thread 123145517002752:​ before the barrier 
-Thread 123145522257920:​ before the barrier 
-   let the flood in 
-Thread 123145522257920:​ after the barrier 
-Thread 123145501237248:​ after the barrier 
-Thread 123145506492416:​ after the barrier 
-Thread 123145511747584:​ after the barrier 
-Thread 123145517002752:​ after the barrier 
-</​code>​ 
- 
-====== Exercices pratiques ====== 
- 
-Pour résoudre le laboratoire,​ utilisez le référentiel github. Pour télécharger le référentiel,​ exécutez la commande git clone https://​github.com/​UPB-FILS/​sde.git dans le terminal. 
- 
-===== Exercice 1 - Print once ===== 
-Dans le fichier main.py dans le répertoire 1-once, nous avons créé un programme qui utilise 10 threads pour afficher le texte 10 fois. Modifiez la fonction ** print_text ** pour qu'​après avoir exécuté le programme, le texte ne s'​affiche qu'une seule fois sur l'​écran. 
- 
-===== Exercice 2 - Magic number ===== 
- 
-Dans le fichier main.py du répertoire 2-magic, on a décrit une fonction qui recherche le premier palindrome dans la plage min-max, où min et max sont deux valeurs que la fonction reçoit comme paramètres,​ en utilisant deux threads. 
- 
-Modifiez le code afin que la variable globale ** magic_number ** contienne la première valeur trouvée (si un thread a trouvé une valeur, la seconde ne stockera plus la valeur trouvée). 
- 
-===== Exercice 3 - Moyenne aritmetique ===== 
- 
- 
-Dans le fichier main.py du répertoire 3-avg, nous utilisons deux threads pour générer des paires min-max, tandis que le programme principal calcule le nombre moyen de la plage générée. Utilisez la variable ** sémaphore ** pour assurer le bon fonctionnement du programme. 
- 
-===== Exercice 4 - Moyenne arithmétique 2 ===== 
- 
-Modifiez le programme de l'​exercice précédent comme suit: 
-   * créez un autre thread qui calcule la moyenne arithmétique et l'​affiche 
-   * utilisez une condition variable pour assurer la synchronisation des threads 
- 
-===== Exercice 5 - Maximum ===== 
- 
-Créez un programme qui lance 10 threads pour générer 10 nombres aléatoires et trouver le maximum de 10 (le maximum est calculé dans le même thread, pas dans le programme principal). Utilisez une structure de barrière pour assurer le bon fonctionnement du programme. 
  
sde/laboratoare/09_python.1586883914.txt.gz · Last modified: 2020/04/14 20:05 by diana.ghindaoanu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0