PROGRAMAREA MULTIPROCESOARELOR 2 SEMAFOARE SINCRONIZAREA THREADURILOR IN WINDOWS

  • Slides: 32
Download presentation
PROGRAMAREA MULTIPROCESOARELOR (2) • SEMAFOARE • SINCRONIZAREA THREAD-URILOR IN WINDOWS • DETECTAREA PARALELISMULUI IN

PROGRAMAREA MULTIPROCESOARELOR (2) • SEMAFOARE • SINCRONIZAREA THREAD-URILOR IN WINDOWS • DETECTAREA PARALELISMULUI IN PROGRAME

SEMAFOARE Primitivele P şi V (Dijkstra): operaţii indivizibile care operează pe o variabilă semafor

SEMAFOARE Primitivele P şi V (Dijkstra): operaţii indivizibile care operează pe o variabilă semafor (Indică numărul de procese care încearcă să intre în secţiunea critică). var s: semaphore; P(s): MUTEXBEGIN(s) s s-1; if s < 0 then begin Blochează procesul executând P(s) şi-l introduce într-o coadă FIFO asociată semaforului s; Reia procesul gata de prioritatea cea mai mare end MUTEXEND;

V(s): MUTEXBEGIN(s) s s+1; if s 0 then begin Dacă există un proces inactiv

V(s): MUTEXBEGIN(s) s s+1; if s 0 then begin Dacă există un proces inactiv asociat cu semaforul s, atunci activează procesul blocat cu prioritatea cea mai mare asociat cu s şi-l introduce într-o listă de procese gata end MUTEXEND Semaforul s iniţializat la valoarea 1 (numărul de procese concurente care se pot afla simultan în secţiunea critică). Dacă s = 0 / 1 => semafor binar (un singur proces la un moment dat este în secţiunea critică). Dacă s = o valoare numerică întreagă => semafor numărător. Utilizare comună : procese concurente schimba date în timpul execuţiei.

Exemplu: procese producător şi consumator - comunică printr-un tampon finit de dimensiune n numit

Exemplu: procese producător şi consumator - comunică printr-un tampon finit de dimensiune n numit BUFFER, de tip coadă circulară cu locaţiile 0, 1, …, n-1. -pointeri c şi p (indicatori de cap al cozii (“head”) şi coadă a cozii (“tail”)); -consumatorul consumă mesaje din capul listei, actualizând c şi preluând mesajul (c indică locaţie vidă înainte de fiecare consum); -producătorul depune mesaje în coadă, actualizând pointerul p înainte de operaţia de adăugare; -iniţial p=c=0 (tampon vid); -empty şi full => starea tamponului (tampon vid, respectiv tampon plin) => de fapt, empty indică producătorului numărul de locaţii disponibile, full indică consumatorului numărul de locaţii de consumat; -producătorul şi consumatorul se suspendă dacă empty=0, respectiv full=0.

shared record begin var p, c: integer; var empty, full: semaphor; var BUFFER[0: n-1]:

shared record begin var p, c: integer; var empty, full: semaphor; var BUFFER[0: n-1]: message end; initial empty = n; full = 0; p = 0; cobegin Producer: begin var m: message; cycle begin produce un mesaj m; P(empty); p (p+1) mod n; BUFFER[p] m; V(full) end;

Consumer: coend begin var m: message; cycle begin P(full); c (c+1) mod n; m

Consumer: coend begin var m: message; cycle begin P(full); c (c+1) mod n; m BUFFER[c]; V(empty); consumă mesajul m end

Operaţiile P şi V extinse => primitivele extinse PE şi VE (Agerwala), indivizibile, operează

Operaţiile P şi V extinse => primitivele extinse PE şi VE (Agerwala), indivizibile, operează fiecare pe un set de semafoare iniţializate la valori pozitive. PE(s 1, s 2, …, sn+1#, …, sn+m#); MUTEXBEGIN if (for all i, 1 i n, si>0) and (for all j, 1 j m, sn+j# = 0) then for all i, 1 i n, si si-1 else Procesul este blocat şi depus într-un set de cozi asociate cu setul de semafoare s 1, s 2, …, sn MUTEXEND VE(s 1, s 2, …, sn); MUTEXBEGIN for all i, 1 i n, si s. I+1; Activează procesul cu prioritatea cea mai mare asociat cu setul de semafoare s 1, s 2, …, sn MUTEXEND

Exemplu: n procese de priorităţi egale şi m resurse la care procesele fac acces

Exemplu: n procese de priorităţi egale şi m resurse la care procesele fac acces exclusiv. Dacă două procese utilizează seturi disjuncte de resurse, acestea se pot executa în paralel: var r 1, r 2, …, rm: semaphore; initial r 1 = r 2 = …= rm = 1; Proces i: begin PE(ra, rb, …, rx); utilizează resursele a, b, … , x; VE(ra, rb, …, rx) end Observatii: -semaforul ri asociat cu resursa i; -dacă primitiva PE s-a încheiat cu succes de către un proces => resursele a, b, … , x sunt disponibile (alocate procesului).

Exemplu: trei procese X, Y şi Z care intră în concurenţă pentru trei echipamente

Exemplu: trei procese X, Y şi Z care intră în concurenţă pentru trei echipamente (cititor R, imprimantă P şi un disc D). Fiecare proces cere simultan două resurse (X cere R şi P, Y cere R şi D, iar Z cere P şi D). var r. R, r. P, r. D: semaphore; initial r. R = r. P = r. D = 1; cobegin Proces X: begin PE(r. R, r. P); utilizează R şi P; VE(r. R, r. P) end; Proces Y: begin PE(r. R, r. D); utilizează R şi D; VE(r. R, r. D) end; Proces Z: begin PE(r. P, r. D); utilizează P şi D; VE(r. P, r. D) end coend

Exemplu: n procese paralele de priorităţi diferite şi o resursă. Procesul Pi are prioritate

Exemplu: n procese paralele de priorităţi diferite şi o resursă. Procesul Pi are prioritate mai mare decât Pi+1. Procesele cer acces la o resursă şi sunt alocate la resursă într-o manieră mutual exclusivă, bazată pe priorităţi. O cerere nu este onorată până ce toate cererile de prioritate mai mare nu au fost rezolvate. var s 1, s 2, …, sn, R: semaphore; initial s 1 = s 2 = …= sn = 0; initial R = 1; cobegin Proces i: begin VE(si); //cererea procesului i// PE(R, s 1#, s 2#, …, si-1#); //verifică dacă resursa este disponibilă si dacă există cereri nesatisfăcute ale unor procese de prioritate mai mare// PE(si); //dacă nu, alocă resursa la procesul i şi retrage cererile nesatisfăcute// Utilizează resursa; VE(R) //eliberează resursa// end coend

Exemplu: sistem biprocesor, pe care rulează un proces supervizor şi unul utilizator. Dacă cele

Exemplu: sistem biprocesor, pe care rulează un proces supervizor şi unul utilizator. Dacă cele două procese intră în competiţie simultan pentru o anumită resursă, procesul supervizor are prioritate mai mare: var s, u, R: semaphore; initial s = u = 0; initial R = 1; cobegin Supervisor: begin VE(s); PE(R); PE(s); Utilizează resursa; VE(R) end User: begin VE(u); PE(R, s#); PE(u); Utilizează resursa; VE(R) end coend

SEMAFOARE IN WINDOWS Crearea unui semafor : HANDLE Create. Semaphore( LPSECURITY_ATTRIBUTES lp. Semaphore. Attributes,

SEMAFOARE IN WINDOWS Crearea unui semafor : HANDLE Create. Semaphore( LPSECURITY_ATTRIBUTES lp. Semaphore. Attributes, LONG l. Initial. Count, LONG l. Maximum. Count, LPCTSTR lp. Name ); unde: lp. Semaphore. Attributes = pointer specificand un descriptor de securitate a semaforului. Daca este NULL, atunci semaforul nu va avea descriptor de securitate. l. Initial. Count = valoarea initiala a contorului asociat semaforului. Se poate initializa cu 0 pentru a impiedica preluarea acestuia de vreun thread in timpul initializarii programului. l. Maximum. Count = este numarul maxim de thread-uri concurente care pot prelua semaforul fara sa fie blocate. lp. Name = numele semaforului.

Apel wait pentru un semafor => decrementeaza contorul cu 1. Cand contorul == 0,

Apel wait pentru un semafor => decrementeaza contorul cu 1. Cand contorul == 0, urmatoarele apeluri wait sunt blocate pana ce un thread elibereaza detinerea semaforului. Functii de asteptare: Wait. For. Single. Object si Wait. For. Multiple. Objects. Starile unui obiect de sincronizare Windows : signaled (disponibil) si notsignaled (detinut). DWORD Wait. For. Single. Object( HANDLE h. Handle, DWORD dw. Milliseconds ); unde: h. Handle = handle-ul obiectului de sincronizare pentru care se asteapta; dw. Milliseconds = timeout, care poate fi 0, INFINITE (-1) sau o valoare pozitiva, in milisecunde. Daca obiectul este signaled, functia intoarce 0 (succes), iar daca este not-signaled functia se blocheaza pana ce obiectul devine disponibil sau expira timpul de timeout. In cazul expirarii timpului de timeout functia intoarce starea WAIT_TIMEOUT. Daca intoarce WAIT_ABANDONED inseamna ca un thread s-a terminat fara se aelibereze obiectul de sincronizare. Daca intoarce valoarea – 1, se apeleaza functia Get. Last. Error.

DWORD Wait. For. Multiple. Objects( DWORD n. Count, const HANDLE* lp. Handles, BOOL b.

DWORD Wait. For. Multiple. Objects( DWORD n. Count, const HANDLE* lp. Handles, BOOL b. Wait. All, DWORD dw. Milliseconds ); unde: n. Count = numarul de obiecte din tablou pentru care se asteapta; lp. Handles = pointer la un tablou de handle-uri de obiecte pentru care se asteapta; b. Wait. All daca are valoarea TRUE asteapta ca toate obiectele sa fie in starea signaled, iar daca are valoarea FALSE functia revine daca oricare obiect ajunge in starea signaled. Pentru un apel wait-any functia intoarce indexul obiectului in starea signaled, o valoare intre 0 si n. Count – 1. dw. Milliseconds = durata timeout in ms.

Cand un thread termina sectiunea critica va apela functia Release. Semaphore, incrementand contorul si

Cand un thread termina sectiunea critica va apela functia Release. Semaphore, incrementand contorul si permitand unui alt thread sa castige semaforul: BOOL Release. Semaphore( HANDLE h. Semaphore, LONG l. Release. Count, LPLONG lp. Previous. Count ); unde: h. Semaphore = handle-ul semaforului care se elibereaza; l. Release. Count = increment la valoarea curenta a contorului asociat semaforului; lp. Previous. Count = optional, pointer la o variabila care primeste ultima valoare a semaforului.

Exemplu: utilizarea unui semafor. HANDLE h. Sema; h. Sema=Create. Semaphore(NULL, 0, 3, NULL); //fara

Exemplu: utilizarea unui semafor. HANDLE h. Sema; h. Sema=Create. Semaphore(NULL, 0, 3, NULL); //fara nume if (h. Sema==NULL) {. . . //eroare de handle }. . . //alte initializari in program Release. Semaphore(h. Sema, 3, NULL); //max. 3 thread-uri pot detine semaforul. . . //acces la semafor Status=Wait. For. Single. Object(h. Sema, INFINITE); if (Status==-1) {. . . //eroare de handle } //succes wait, contorul semaforului decrementat cu 1 //acces la resursele partajate. . . //eliberare semafor Release. Semaphore(h. Sema, 1, NULL); //incrementare contor cu 1. . .

Mutex („mutual exclusion”): asigura dreptul exclusiv de utilizare a unei resurse. Operatii cu mutex:

Mutex („mutual exclusion”): asigura dreptul exclusiv de utilizare a unei resurse. Operatii cu mutex: -acces (obtinere) la mutex („lock”). -daca mutex liber -> se ocupa mutex si se intoarce imediat controlul la procesul apelant, intr-o operatie atomica; -daca mutex ocupat -> procesul apelant trebuie sa astepte eliberarea acestuia. Protocolul de asteptare : „sleep-wait” sau „busy wait”/”sleep-wait”. -eliberare mutex („unlock” sau „release”) cand procesul a terminat accesul la resursa partajata.

Crearea unui obiect mutex: HANDLE Create. Mutex( LPSECURITY_ATTRIBUTES lp. Mutex. Attributes, BOOL b. Initial.

Crearea unui obiect mutex: HANDLE Create. Mutex( LPSECURITY_ATTRIBUTES lp. Mutex. Attributes, BOOL b. Initial. Owner, LPCTSTR lp. Name ); unde: lp. Mutex. Attributes = pointer la o structura de atribute de securitate. Daca NULL mutex este creat fara un atribut de securitate. b. Initial. Owner = detinere initiala. Daca TRUE, creatorul mutex-ului va prelua imediat detinerea acestuia, altfel mutex este disponibil pentru primul apelant de functie wait asupra mutex-ului. lp. Name = sir de caractere cu numele mutex, pentru a putea fi accesat si de alte procese. Daca NULL, atunci mutex este fara nume.

Preluarea mutex: una din cele doua functii wait. Dupa preluarea mutex, threadul executa codul

Preluarea mutex: una din cele doua functii wait. Dupa preluarea mutex, threadul executa codul din regiunea protejata de mutex. Pentru eliberarea mutex: BOOL Release. Mutex( HANDLE h. Mutex );

Exemplu: utilizare mutex. HANDLE h. Mutex; char * sz. Mutex. Name=”mutex 1”; h. Mutex=Create.

Exemplu: utilizare mutex. HANDLE h. Mutex; char * sz. Mutex. Name=”mutex 1”; h. Mutex=Create. Mutex(NULL, FALSE, sz. Mutex. Name): if (h. Mutex==NULL) {. . . //eroare de handle }. . . if (Wait. For. Single. Object(h. Mutex, INFINITE)!=0) {. . . //eroare de handle else { //incepe sectiunea critica. . . Release. Mutex(h. Mutex); //sfarsit sectiune critica, elibereaza mutex }

Alte obiecte de sincronizare in Windows Sectiune critica: la un moment dat un singur

Alte obiecte de sincronizare in Windows Sectiune critica: la un moment dat un singur thread poate prelua obiectul sectiune critica. La o incercare de preluare => succes sau thread-ul este blocat pana ce obiectul de sincronizare este disponibil (fara timeout). Operatiile de initializare, preluare, eliberare si distrugere a unui obiect sectiune critica : void Initialize. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section ); void Enter. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section ); void Leave. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section ); void Delete. Critical. Section( LPCRITICAL_SECTION lp. Critical. Section );

Eveniment = un mecanism de sincronizare intre procese sau thread-uri concurente => se anunta

Eveniment = un mecanism de sincronizare intre procese sau thread-uri concurente => se anunta aparitia unui anumit eveniment (indeplinirea unei anumite conditii). Crearea unui obiect eveniment: HANDLE Create. Event( LPSECURITY_ATTRIBUTES lp. Event. Attributes, BOOL b. Manual. Reset, BOOL b. Initial. State, LPCTSTR lp. Name ); unde: lp. Event. Attributes = pointer specificand atributul de securitate, indicand daca procesele create ulterior vor mosteni handle-ul. b. Manual. Reset = indica tipul obiectului care va fi creat. Daca TRUE, atunci obiectul este cu resetare manuala (se apeleaza explicit Set. Event pentru a-l trece in starea signaled si Reset. Event pentru a-l trece in starea not-signaled. Daca FALSE, atunci obiectul este cu resetare automata: apelul Set. Event il trece in stare signaled, lasand un thread in asteptare sa ruleze, apoi reseteaza automat obiectul in starea not-signaled. b. Initial. State = reprezinta starea initiala signaled sau not-signaled. Lp. Name= numele evenimentului.

Functiile pentru setarea si resetarea unui eveniment: BOOL Set. Event( HANDLE h. Event );

Functiile pentru setarea si resetarea unui eveniment: BOOL Set. Event( HANDLE h. Event ); BOOL Reset. Event( HANDLE h. Event ); BOOL Pulse. Event( HANDLE h. Event ); Pulse. Event in mod automat seteaza apoi reseteaza starea obiectului eveniment.

DETECTAREA PARALELISMULUI IN PROGRAME Reprezentarea ierarhică a unui program organizat secvenţial:

DETECTAREA PARALELISMULUI IN PROGRAME Reprezentarea ierarhică a unui program organizat secvenţial:

Considerând T 1, T 2, T 3 instrucţiuni ale unui program secvenţial, dacă execuţia

Considerând T 1, T 2, T 3 instrucţiuni ale unui program secvenţial, dacă execuţia lui T 3 este independentă de ordinea de execuţie a instrucţiunilor T 1 şi T 2, atunci există paralelism între T 1 şi T 2.

Pentru ca procese organizate secvenţial să poată fi executate în paralel este necesar să

Pentru ca procese organizate secvenţial să poată fi executate în paralel este necesar să satisfacă condiţia lui Bernstein (două seturi de variabile pentru fiecare proces Ti): 1) setul de citire Ii, reprezentat de toate locaţiile de memorie din care Ti execută citire ca primă operaţie ce implică aceste locaţii; 2) setul de scriere Oi, reprezentat de toate locaţiile în care Ti execută scrieri. Condiţiile sub care două procese secvenţiale T 1 şi T 2 pot fi executate ca două procese independente şi concurente : 1) Locaţiile din I 1 nu trebuie modificate de operaţii de scriere din O 2: I 1 O 2 = 2) Prin simetrie: I 2 O 1 = În plus, la începerea execuţiei lui T 3, independent de modul de execuţie (paralel sau secvenţial) al proceselor T 1 şi T 2, I 3 trebuie să fie independent de operaţiile de memorare din T 1 şi T 2: (O 1 O 2 ) I 3 =

Exemplu: se consideră trei instrucţiuni pentru evaluarea a trei expresii matriciale, unde A, B,

Exemplu: se consideră trei instrucţiuni pentru evaluarea a trei expresii matriciale, unde A, B, C, D, X, Y, Z sunt matrici n*n: T 1: X ( A + B ) * ( A – B ) T 2: Y ( C – D ) * ( C + D )-1 T 3: Z X + Y Se pot determina următoarele seturi: I 1= A, B , I 2= C, D , O 2= X , O 2= Y. Deoarece I 1 O 2 = , I 2 O 1 = , O 1 O 2 = => procesele T 1 şi T 2 se pot executa în paralel. Procesul T 3 nu se poate executa în paralel cu procesele T 1 şi T 2, deoarece I 3 O 2 , şi I 3 O 2 . Programul concurent pentru execuţia proceselor T 1, T 2 şi T 3 este prezentat în continuare: begin cobegin X ( A + B ) * ( A – B ); Y ( C – D ) * ( C + D )-1 coend; Z X + Y end

Proiectarea programelor de aplicaţii -> tehnică de bază: descompunerea algoritmilor pentru execuţie paralelă. Etape:

Proiectarea programelor de aplicaţii -> tehnică de bază: descompunerea algoritmilor pentru execuţie paralelă. Etape: 1) Partiţionarea - împărţirea algoritmului în proceduri, module şi procese. 2) Asignarea - alocarea acestor unităţi de calcul la procesoarele sistemului pe care se rulează algoritmul respectiv. Exemplu: algoritmul de calcul al histogramei unei imagini, scris pentru un uniprocesor, care va fi descompus şi restructurat pentru un sistem multiprocesor. Fiecare pixel este un întreg în intervalul [0, b-1], cu 2 b 256 (valoare tipică pentru b este reprezentarea pe opt biţi, deci b=256). Algoritmul utilizează un tablou histog[0: b 1] pentru contorizarea nivelelor de gri din imagine, acestea având valorile 0, 1, …, b-1. Imaginea este reprezentată prin tabloul bidimensional pixel[0: m-1; 0: n-1], având m rânduri şi n coloane de pixeli.

var pixel[0: m-1; 0: n-1]: pixel; var histog[0: b-1]: integer; initial histog[0: b-1]=0; //iniţializare

var pixel[0: m-1; 0: n-1]: pixel; var histog[0: b-1]: integer; initial histog[0: b-1]=0; //iniţializare contoare// for i 0 until m-1 do for j 0 until n-1 do histog[pixel[i, j]]+1 end Complexitatea de timp: O(mn). Programul restructurat - împărţind imaginea iniţială în p segmente egale şi disjuncte, fiecare cu m/p=s rânduri de pixeli (p divide pe m). => p procese concurente, fiecare calculând histograma unui segment distinct al imaginii. Procesele partajează vectorul histog[0: b-1].

var histog[0: b-1]: integer shared; //declararea variabilelor partajate// initial histog[0: b-1]=0; //iniţializare contoare// parfor

var histog[0: b-1]: integer shared; //declararea variabilelor partajate// initial histog[0: b-1]=0; //iniţializare contoare// parfor i 1 until p do begin var pixel[(i-1) · s: s · i-1; 0: n-1]: pixel; for k (i-1) · s until s· i-1 do for j 0 until n-1 do csect histog[pixel[k, j]] do histog[pixel[k, j]]+1 end

O creştere de performanţă: fiecare proces dispune de setul său de b contoare pentru

O creştere de performanţă: fiecare proces dispune de setul său de b contoare pentru a genera histograma locală a segmentului propriu din imagine. var histog[0: b-1]: integer; initial histog[0: b-1]=0; var lhistog[1: p; 0: b-1]: integer; //numărătoare locale// initial lhistog[1: p; 0: b-1]=0; //iniţializare contoare locale// parfor i 1 until p do begin var pixel[(i-1) · s: s· i-1; 0: n-1]: pixel; var lhistog[i, 0: b-1]: integer; for k (i-1) · s until s· i-1 do for j 0 until n-1 do lhistog[i, pixel[k, j]]+1 end end for j 0 until b-1 do for i 1 until p do histog[j]+lhistog[i, j] end