This shows you the differences between two versions of the page.
asc:laboratoare:03 [2022/03/25 13:14] giorgiana.vlasceanu [Resurse] |
asc:laboratoare:03 [2023/03/17 01:00] (current) emil.slusanschi [Exerciții] |
||
---|---|---|---|
Line 29: | Line 29: | ||
Capabilitatea obiectului //Event// de a bloca execuția thread-urilor și de a le debloca pe toate în același timp poate fi folosită în locul semaforului, ca **mecanism de blocare/deblocare**, la implementarea unei bariere ne-reentrante. Avantajul acestei soluții față de cea cu semafor este claritatea. //Event//-ul se ocupă de blocarea/deblocarea thread-urilor, iar contorul și //lock//-ul țin evidența thread-urilor intrate în barieră. Soluția cu semafor conține însă două contoare (unul dat de semaforul însuși), care trebuie să rămână corelate, iar **mecanismul de blocare/deblocare** este combinat cu **contorul**, complicând astfel analiza implementării. | Capabilitatea obiectului //Event// de a bloca execuția thread-urilor și de a le debloca pe toate în același timp poate fi folosită în locul semaforului, ca **mecanism de blocare/deblocare**, la implementarea unei bariere ne-reentrante. Avantajul acestei soluții față de cea cu semafor este claritatea. //Event//-ul se ocupă de blocarea/deblocarea thread-urilor, iar contorul și //lock//-ul țin evidența thread-urilor intrate în barieră. Soluția cu semafor conține însă două contoare (unul dat de semaforul însuși), care trebuie să rămână corelate, iar **mecanismul de blocare/deblocare** este combinat cu **contorul**, complicând astfel analiza implementării. | ||
- | <code python simple_barrier_event.py> | + | <code python task01.py> |
from threading import * | from threading import * | ||
Line 120: | Line 120: | ||
* [[https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.map|map]] - aplică o funcție pe o structură de date iterabilă, cum se poate observa în exemplul următor | * [[https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.map|map]] - aplică o funcție pe o structură de date iterabilă, cum se poate observa în exemplul următor | ||
- | <code Python| threadpool_example.py> | + | <code Python| task02.py> |
from concurrent.futures import ThreadPoolExecutor | from concurrent.futures import ThreadPoolExecutor | ||
from concurrent.futures import as_completed | from concurrent.futures import as_completed | ||
Line 153: | Line 153: | ||
De multe ori este necesar ca un grup de thread-uri să ajungă toate într-un anumit punct al execuției (ex: fiecare thread a calculat un rezultat intermediar al algoritmului) și numai după aceea să își continue execuția (ex: rezultatele intermediare sunt partajate de toate thread-urile în partea următoare a algoritmului). Mecanismul de sincronizare potrivit pentru asemenea situații este **bariera**. | De multe ori este necesar ca un grup de thread-uri să ajungă toate într-un anumit punct al execuției (ex: fiecare thread a calculat un rezultat intermediar al algoritmului) și numai după aceea să își continue execuția (ex: rezultatele intermediare sunt partajate de toate thread-urile în partea următoare a algoritmului). Mecanismul de sincronizare potrivit pentru asemenea situații este **bariera**. | ||
- | Începând cu Python 3.2 în modulul //threading// a fost introdusă clasa //[[https://docs.python.org/3/library/threading.html#barrier-objects|Barrier]]//, acesta fiind o barieră reentrantă implementată folosind variabile condiție ([[https://github.com/python/cpython/blob/master/Lib/threading.py#L580|cod sursă]]). În această secțiune vom prezenta două variante pentru implementarea unui astfel de obiect. | + | Începând cu Python 3.2 în modulul //threading// a fost introdusă clasa //[[https://docs.python.org/3/library/threading.html#barrier-objects|Barrier]]//, acesta fiind o barieră reentrantă implementată folosind variabile condiție ([[https://github.com/python/cpython/blob/bb396eece44036a71427e7766fbb8e0247373102/Lib/threading.py#L649|cod sursă]]). În această secțiune vom prezenta două variante pentru implementarea unui astfel de obiect. |
Ce trebuie să ofere o barieră? | Ce trebuie să ofere o barieră? | ||
Line 167: | Line 167: | ||
Putem implementa o barieră folosind un semafor inițializat cu 0 (**mecanismul de blocare/deblocare**) și un contor al numărului de thread-uri care mai trebuie să ajungă la barieră (**contorul**), inițializat cu numărul de thread-uri utilizate. Semaforul este folosit pentru a bloca execuția thread-urilor. Contorul este decrementat de fiecare thread care ajunge la barieră și reprezintă numărul de thread-uri care au mai rămas de ajuns. Fiind o variabilă partajată, modificarea lui trebuie bineînțeles protejată de un //lock//. În momentul în care ultimul thread decrementează contorul, acesta va avea valoarea 0, semnalizând faptul că toate thread-urile au ajuns la barieră. Ultimul thread va incrementa astfel semaforul (**deblocarea**) și va debloca toate thread-urile blocate. | Putem implementa o barieră folosind un semafor inițializat cu 0 (**mecanismul de blocare/deblocare**) și un contor al numărului de thread-uri care mai trebuie să ajungă la barieră (**contorul**), inițializat cu numărul de thread-uri utilizate. Semaforul este folosit pentru a bloca execuția thread-urilor. Contorul este decrementat de fiecare thread care ajunge la barieră și reprezintă numărul de thread-uri care au mai rămas de ajuns. Fiind o variabilă partajată, modificarea lui trebuie bineînțeles protejată de un //lock//. În momentul în care ultimul thread decrementează contorul, acesta va avea valoarea 0, semnalizând faptul că toate thread-urile au ajuns la barieră. Ultimul thread va incrementa astfel semaforul (**deblocarea**) și va debloca toate thread-urile blocate. | ||
| | ||
- | <code python simple_barrier_semaphore.py> | + | <code python task03.py> |
from threading import * | from threading import * | ||
Line 226: | Line 226: | ||
s.a.m.d.... | s.a.m.d.... | ||
- | <code python reusable_barrier_semaphore.py> | + | <code python task04.py> |
from threading import * | from threading import * | ||
Line 270: | Line 270: | ||
O altă utilizare a obiectului //Condition// poate fi văzută în implementarea barierei reentrante, ca **mecanism de blocare/deblocare**. Bariera poate fi implementată cu un singur obiect deoarece prezența implicită a lock-ului în operațiile obiectului //Condition//, împreună cu funcționarea atomică a metodei //wait()// ne permit evitarea problemelor ce apar la refolosirea obiectelor //Event//. Pe lângă notificarea thread-urilor de îndeplinirea condiție barierei, putem folosi obiectul //Condition// și pentru protejarea resursei partajate (**contorul** de thread-uri blocate), eliminând astfel necesitatea unui //Lock// separat. | O altă utilizare a obiectului //Condition// poate fi văzută în implementarea barierei reentrante, ca **mecanism de blocare/deblocare**. Bariera poate fi implementată cu un singur obiect deoarece prezența implicită a lock-ului în operațiile obiectului //Condition//, împreună cu funcționarea atomică a metodei //wait()// ne permit evitarea problemelor ce apar la refolosirea obiectelor //Event//. Pe lângă notificarea thread-urilor de îndeplinirea condiție barierei, putem folosi obiectul //Condition// și pentru protejarea resursei partajate (**contorul** de thread-uri blocate), eliminând astfel necesitatea unui //Lock// separat. | ||
- | <code python reusable_barrier_condition.py> | + | <code python task05.py> |
from threading import * | from threading import * | ||
Line 304: | Line 304: | ||
</code> | </code> | ||
- | Puteți găsi aici modul de implementare a [[https://github.com/python/cpython/blob/master/Lib/threading.py#L580|barierei cu condition]] din sursele oficiale. | + | Puteți găsi aici modul de implementare a [[https://github.com/python/cpython/blob/bb396eece44036a71427e7766fbb8e0247373102/Lib/threading.py#L649|barierei cu condition]] din sursele oficiale. |
===== Exerciții ===== | ===== Exerciții ===== | ||
- | | + | |
- | ** Task 1-Condition ** | + | |
+ | **Task 0** - Rulați exemplele task01.py task02.py task03.py task04.py task05.py. | ||
+ | |||
+ | **Task 1** Condition | ||
| | ||
Pornind de la fișierul ''event.py'', înlocuiți obiectele //Event// ''work_available'' și ''result_available'' cu o singură variabilă condiție, păstrand funcționalitatea programului intactă. | Pornind de la fișierul ''event.py'', înlocuiți obiectele //Event// ''work_available'' și ''result_available'' cu o singură variabilă condiție, păstrand funcționalitatea programului intactă. | ||
| | ||
- | ** Task 2-Events ** | + | **Task 2** Events |
| | ||
- | Rulați fișierul ''broken-event.py''. Încercați diferite valori pentru //sleep()//-ul de la linia 47. Încercați eliminarea apelului //sleep()//. Ce observați? Precizați secvența _minimă_ de intercalare a apelurilor ''set'' și ''wait'' pe cele două obiecte //Event// care generează comportamentul observat. | + | Rulați fișierul ''broken_event.py''. Încercați diferite valori pentru //sleep()//-ul de la linia 48. Încercați eliminarea apelului //sleep()//. Ce observați? Precizați secvența _minimă_ de intercalare a apelurilor ''set'' și ''wait'' pe cele două obiecte //Event// care generează comportamentul observat. |
* Folosiți ''Ctrl+\'' pentru a opri un program blocat. | * Folosiți ''Ctrl+\'' pentru a opri un program blocat. | ||
* Folosiți //sleep()// pentru a forța diferite intercalări ale thread-urilor. | * Folosiți //sleep()// pentru a forța diferite intercalări ale thread-urilor. | ||
Line 320: | Line 323: | ||
* :!: Datorită intercalărilor thread-urilor este posibil ca //print//-urile să nu reflecte ordinea exactă a operațiilor. Rulați de mai multe ori pentru a putea prinde problema. | * :!: Datorită intercalărilor thread-urilor este posibil ca //print//-urile să nu reflecte ordinea exactă a operațiilor. Rulați de mai multe ori pentru a putea prinde problema. | ||
| | ||
- | ** Task 3-ThreadPoolExecutor ** | + | **Task 3** ThreadPoolExecutor - Completați fișierul ''dna.py''. |
| | ||
Folosind un pool de thread-uri căutați o secvență de ADN într-un set de eșantioane de ADN (DNA samples). Creați-vă un modul în care: | Folosind un pool de thread-uri căutați o secvență de ADN într-un set de eșantioane de ADN (DNA samples). Creați-vă un modul în care: | ||
Line 335: | Line 338: | ||
-Folosind [[https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.submit|submit]] sau [[https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.map|map]] dați spre execuție funcția de căutare. | -Folosind [[https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.submit|submit]] sau [[https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Executor.map|map]] dați spre execuție funcția de căutare. | ||
-Afișați rezultatele | -Afișați rezultatele | ||
- | * păstrați ce întorc apelurile către submit sau rezultatul lui map într-o variabilă și apoi iterați pe elementele din ea, afișând fiecare rezultat) | + | * păstrați ce întorc apelurile către submit sau rezultatul lui map într-o variabilă și apoi iterați pe elementele din ea, afișând fiecare rezultat |
* puteți vedea în [[asc:laboratoare:03#threadpoolexecutor|exemplul din laborator]] cum se afișează rezultatele unui map, sau în documentație pentru [[https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor-example|submit]] | * puteți vedea în [[asc:laboratoare:03#threadpoolexecutor|exemplul din laborator]] cum se afișează rezultatele unui map, sau în documentație pentru [[https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor-example|submit]] | ||
* dacă nu sunteți familiari cu sintaxa de one-liners folosită în documentație, puteți folosi simple for-uri, va creați o lista goală înainte, și la fiecare iterație din for adăugați în ea rezultatul metodei submit. După aceea mai faceți un for în care afișați elementele din lista, apelând .result() pe fiecare din ele. | * dacă nu sunteți familiari cu sintaxa de one-liners folosită în documentație, puteți folosi simple for-uri, va creați o lista goală înainte, și la fiecare iterație din for adăugați în ea rezultatul metodei submit. După aceea mai faceți un for în care afișați elementele din lista, apelând .result() pe fiecare din ele. | ||
Line 348: | Line 351: | ||
* <html><a class="media mediafile mf_pdf" href=":asc:lab3:index?do=export_pdf">PDF laborator</a></html> | * <html><a class="media mediafile mf_pdf" href=":asc:lab3:index?do=export_pdf">PDF laborator</a></html> | ||
- | * {{:asc:lab3:lab3-skel.zip|Schelet laborator}} | + | * {{:asc:laboratoare:lab3-skel_2023.zip|Schelet laborator}} |
- | * {{:asc:lab3:lab3-sol.zip|Soluție laborator}} | + | <hidden> * {{:asc:lab3:lab3-sol.zip|Soluție laborator}} </hidden> |