Fie urmtorul program: int i = 0; void *fir(void* arg){ i++; } int main(){ pthread_t th[3]; int j; for(j = 0; j < 3; j++){ int ret = pthread_create(th[j]! "#$$! fir! "#$$); if(ret){ printf(%eroare&n%); e'it(()); } } for(j = 0; j < 3; j++){ pthread_join(th[j]! "#$$); } printf(%*a+oarea +,i i e-te .d&n%! i); } Programul este foarte simplu: firul principal trei fire de execuie care incrementeaz o variabil global i, iniializat cu 0. La final, dup ce ateapt terminarea celor trei fire, firul principal afieaz valoarea lui i. i aici apare problema, deoarece programul nu este determinist, valorile posibile afiate fiind: !, " sau #. !
$cest lucru se datoreaz faptului c instruciunea de incrementare nu se execut atomic. %a const de fapt din urmtoarea secven de operaii atomice: mov eax,&i' add eax,! mov &i',eax (aloarea ! se obine dac ultimul fir care salveaz valoarea lui i a )ncrcat valoarea 0. *n scenariu posibil este: ! +ei, din motive pe care o s le )nelegei mai bine dup parcurgerea capitolului despre planificarea proceselor, valorile ! i " sunt greu de obinut folosind un singur procesor. +ar dac firele care modific valoarea variabilei i ar fi mai ,consistente, probabilitatea de a obine valori distincte ar crete. Fir ! mov eax,&i' add eax,! mov &i',eax Fir " mov eax,&i' add eax,! mov &i',eax Fir # mov eax,&i' add eax,! mov &i',eax (aloarea " se poate obine cu urmtorul scenariu: Fir ! mov eax,&i' add eax,! mov &i',eax Fir " mov eax,&i' add eax,! mov &i',eax Fir # mov eax,&i' add eax,! mov &i',eax Pe c-nd valoarea # nu se poate obine numai cu un scenariu )n care toate cele trei instruciuni se execut atomic: Fir ! mov eax,&i' add eax,! mov &i',eax Fir " mov eax,&i' add eax,! mov &i',eax Fir # mov eax,&i' add eax,! mov &i',eax +e fapt cele trei instruciuni definesc o zon critic, )n care nu poate fi activ dec-t un singur proces )n orice moment. *nul din mecanismele implementate )n Linux care ne permite definirea unei zone critice este mutex.ul. Mutex *n mutex este dispozitiv care nu poate avea dec-t dou stri: blocat de un singur fir de execuie sau neblocat de nici un fir de execuie. *n mutex poate fi iniializat folosind apelul: int pt/read0mutex0init1pt/read0mutex0t 2mutex, const pt/read0mutexattr0t 2mutexattr34 $cest apel iniializeaz obiectul de la locaia memorat )n mutex conform atributelor specificate cu a5utorul celui de.al doilea argument " . Linux6/reads nu suport dec-t un singur atribut, mutex_kind. *n mutex poate fi : fast 1implicit3, recursive sau error c/ec7ing. *n mutex mai poate fi iniializat folosind # constante: pt/read0mutex0t fastmutex 8 P69:%$+0;*6%<0=>=6=$L=?%:4 pt/read0mutex0t recmutex 8 P69:%$+0:%@*:A=(%0;*6%<0=>=6=$L=?%:0>P4 pt/read0mutex0t errc/7mutex 8 P69:%$+0%::B:@9%@C0;*6%<0=>=6=$L=?%:0>P4 $pelul )ntoarce )ntotdeauna 0. Pentru a bloca un mutex avem dou variante: int pt/read0mutex0loc71pt/read0mutex0t 2mutex34 int pt/read0mutex0trDloc71pt/read0mutex0t 2mutex34 Primul apel bloc/eaz mutex.ul specificat de parametru. +ac mutexul era neblocat ele devine blocat de firul apelant i funcia )ntoarce imediat. +ac mutexul era blocat de un alt fir, firul apelant este suspendat p-n c-nd este acesta este deblocat. +ac )ns mutexul era de5a blocat c/iar de firul apelant, comportamentul apelului depinde de tipul mutexului: fast . firul apelant este suspendat p-n c-nd mutexul va fi deblocat, ceea ce poate fi o mare problem. recursive E apelul reuete, mutexul fiind blocat )nc o dat, el trebuind ,deblocat, de tot at-tea ori de c-te ori a fost blocat pentru a putea fi blocat i de un alt fir. " +ac al doilea argument este >*LL se folosesc atributele implicite. error c/ec7ing E apelul )ntoarce imediat cu eroarea %+%$+LC. @el de.al doilea apel, pthread_mutex_trylock, se comport aproape identic cu primul, singura diferen const-nd )n aceea c nu suspend niciodat firul apelant, )ntorc-nd imediat codul de eroare %F*AG. +up ce blocai un mutex, )n general este bine s.l i deblocai, c-ndva. Pentru aceasta putei folosi numai: int pt/read0mutex0unloc71pt/read0mutex0t 2mutex34 Hn principiu se presupune c firul care )ncearc s debloc/eze mutexul este cel care l.a blocat. +ar i aici, comportamentul apelului depinde de tipul mutexului: fast E )l debloc/eaz )ntotdeauna, indiferent de cine )l blocase anterior. recursive E se decrementeaz numrul de blocri i doar atunci c-nd acest numr a5unge 0 este mutexul considerat liber. error c/ec7ing E numai )n acest caz se verific nu numai c mutexul era blocat, dar i dac era blocat c/iar de firul care dorea s.l debloc/eze. +ac nu e aa, se )ntoarce un cod de eroare ls-nd mutexul neatins. @omportamentul )n cazul fast i recursive nu este portabil. +ac nu mai avei nevoie de mutex, )l putei distruge: int pt/read0mutex0destroD1pt/read0mutex0t 2mutex34 Hn principiu, dac mutexul era neblocat, resursele acestuia sunt eliberate, dar cum )n Linux un mutex nu are nici o resurs asociat, apelul nu face dec-t s verifice dac mutexul era liber. Hn caz de succes, apelurile de mai sus )ntorc 0. $cum putem prote5a incrementarea variabilei i: int i = 0; pthread_m,te'_t m,t, = /012345_6#037_8"8084$8932; void *fir(void* arg){ pthread_m,te'_+oc:(m,t,); i++; pthread_m,te'_,n+oc:(m,t,); } int main(){ pthread_t th[3]; int j; for(j = 0; j < 3; j++){ int ret = pthread_create(th[j]! "#$$! fir! "#$$); if(ret){ printf(%eroare&n%); e'it(()); } } for(j = 0; j < 3; j++){ pthread_join(th[j]! "#$$); } printf(%*a+oarea +,i i e-te .d&n%! i); } Semafoare 3 +ei toat lumea tie ce sunt acelea semafoare o s )ncercm s le predefinim. Aemaforul este un contor asupra cruia nu se pot efectua dec-t dou operaii atomice: incrementare. ateptare ca semaforul s devin mai mare ca 0 i apoi decrementare. *n semafor este iniializat cu a5utorul apelului: Iinclude Jsemap/ore./K int sem0init1sem0t 2sem, int ps/ared, unsigned int value34 (aloarea contorului este iniializat de cel de.al treilea argument. @el de.al doilea argument specific dac semaforul este local procesului curent 1ps/ared este 03 sau este parta5at de mai multe procese 1ps/ared este diferit de 03. +eocamdat acest argument trebuie s fie 0 deoarece Linux6/reads nu suport semafoare parta5ate de mai multe procese. Primul argument este un pointer la semaforul care trebuie iniializat. Pentru a decrementa semaforul avem dou variante: int sem0Lait1sem0t 2 sem34 int sem0trDLait1sem0t 2 sem34 +ac semaforul este 0, sem_wait bloc/eaz firul apelant p-n c-nd semaforul devine mai mare dec-t 0, dup care )l decrementeaz printr.o operaie atomic. $ltfel, semaforul este decrementat i apelul )ntoarce imediat, lucru care se )nt-mpl i )n cazul apelului sem_trywait. +iferena apare atunci c-nd semaforul este 0, sem_trywait )ntorc-nd imediat eroarea %$M$=> N , ls-nd semaforul neatins. Hn caz de succes, ambele )ntorc 0. Pentru a incrementa semaforul exist apelul: int sem0post1sem0t 2 sem34 $ceast funcie nu bloc/eaz niciodat firul apelant, )ntorc-nd 0 )n caz de succes. # Hn aceast seciune sunt descrise semafoarele PBA=< !00#.!b. N set-nd errno Bcazional, este nevoie s aflai ce valoare are contorul unui semafor: int sem0getvalue1sem0t 2 sem, int 2 sval34 $ceast valoare este memorat )n adresa spre care indic parametrul sval. Hn ceea ce privete urmtorul apel, int sem0destroD1sem0t 2 sem34 comportamentul su este similar cu cel care distruge un mutex. Condiii @ondiiile sunt mecanisme de sincronizare similare cu apelurile sleep i La7eup descrise la curs, av-nd )ns protecii )mpotriva problemelor acestora prin asocierea cu un mutex. B s )ncep cu iniializarea: pt/read0cond0t cond 8 P69:%$+0@B>+0=>=6=$L=?%:4 int pt/read0cond0init1pt/read0cond0t 2cond, pt/read0condattr0t 2cond0attr34 +eoarece Linux6/reads nu suport atribute pentru condiii nu sunt multe de spus despre iniializareO dec-t c face iniializarea. Pentru a semnaliza un fir care ateapt o variabil condiie se folosete: int pt/read0cond0signal1pt/read0cond0t 2cond34 +ac mai multe fire sunt suspendate )n ateptarea condiiei numai unul dintre ele este repornit, dar nu se specific care. +ac dorii s repornii toate firele, atunci vor fi semnalizate toate: int pt/read0cond0broadcast1pt/read0cond0t 2cond34 Pentru a atepta dup o condiie: int pt/read0cond0Lait1pt/read0cond0t 2cond, pt/read0mutex0t 2mutex34 $cest apel debloc/eaz mutexul i ateapt pentru ca variabila condiie s fie semnalizat, totul )ntr.o operaie atomic. ;utexul trebuie s fie blocat de firul apelant )n momentul apelului. Hn momentul )n care firul este repornit, mai )nt-i acesta ateapt s rebloc/eze mutexul. +ac nu dorii ca firul s fie suspendat la nesf-rit putei pune o limit de timp pentru ateptare: int pt/read0cond0timedLait1pt/read0cond0t 2cond, pt/read0mutex0t 2mutex, const struct timespec 2abstime34 +ac se atinge limita de timp fr ca nimeni s semnalizeze condiia, apelul )ntoarce eroarea %6=;%+B*6 i mutexul este reblocat. Parametrul abstime specific un timp absolut, cu aceeai origine cu time i gettimeofdaD: lui 0 )i corespunde 00:00:00 M;6, ianuarie !, !PQ0. Probleme Propuse. !. A se determine dac un element apare )ntr.un vector oarecare, folosind n procesoare. ". A se determine pe ce poziii apare un element )ntr.un vector oarecare, folosind n procesoare. #. B resurs poate fi utilizat de dou tipuri de procese: albe i negre. $tunci c-nd resursa este folosit de procese albe ea nu mai poate fi folosit de procese negre. %ste valabil i reciproca. A se implementeze accesul la resurs, evit-ndu.se )nfometarea. N. A se sorteze elementele unui vector cu n procesoare. R. A se genereze toate numerele prime mai mici dec-t o anumit valoare, folosind n procesoare. S. A se coordoneze semafoarele de la captul unui tunel cu o singur band, folosindu.se de senzorii de la fiecare capete care semnalizeaz intrarea i ieirea mainilor, astfel )nc-t s nu se permit intrarea pe la un capt c-t timp mai sunt maini )n tunel merg-nd )n sens invers. ;ainile s fie simulate cu a5utorul firelor de execuie.