Sunteți pe pagina 1din 14

Threads - Fire de executie

Introducere
Executia planificata a proceselor presupune ca, la momente de timp determinate de algoritmul folosit, procesorul sa fie "luat" de la procesul care tocmai se executa si sa fie "dat" unui alt proces. Aceasta comutare intre procese (process switching) este o operatie consumatoare de timp, deoarece trebuie "comutate" toate resursele care apartin proceselor: trebuie salvati si restaurati toti registrii procesor, trebuie (re)mapate zonele de memorie care apartin de noul proces etc. n concept interesant care se regaseste in toate sistemele de operare moderne este acela de fir de executie (thread) in interiorul unui proces. !irele de executie sunt uneori numite procese usoare (lightweight processes), sugerandu"se asemanarea lor cu procesele, dar si, intr"un anume sens, deosebirile dintre ele. n fir de executie trebuie vazut ca un flux de instructiuni care se executa in interiorul unui proces. n proces poate sa fie format din mai multe asemenea fire, care se executa in paralel, avand, insa, in comun toate resursele principale caracteristice procesului. #rin urmare, in interiorul unui proces, firele de executie sunt entitati care ruleaza in paralel, impartind intre ele zona de date si executand portiuni distincte din acelasi cod. $eoarece zona de date este comuna, toate variabilele procesului vor fi vazute la fel de catre toate firele de executie, orice modificare facuta de catre un fir devenind vizibila pentru toate celelalte. %eneralizand, un proces, asa cum era el perceput in lucrarile de laborator precedente, este de fapt un proces format dintr"un singur fir de executie. &a nivelul sistemului de operare, executia in paralel a firelor de executie este obtinuta in mod asemanator cu cea a proceselor, realizandu"se o comutare intre fire, conform unui algoritm de planificare. 'pre deosebire de cazul proceselor, aici comutarea poate fi facuta mult mai rapid, deoarece informatiile memorate de catre sistem pentru fiecare fir de executie sunt mult mai putine decat in cazul proceselor, datorita faptului ca firele de executie au foarte putine resurse proprii. #ractic, un fir de executie poate fi vazut ca un numarator de program, o stiva si un set de registri, toate celelalte resurse (zona de date, identificatori de fisier etc) apartinand procesului in care ruleaza si fiind exploatate in comun. (onceptul de process " unitatea elementara de alocare a resurselor utilizatorilor. (onceptul de fir de executie (sau thread) " unitatea elementara de planificare )ntr"un sistem. (a si procesele, firele de executie reprezinta un mecanism prin care se permite sa se execute simultan mai multe instructiuni . n fir de executie exista )n cadrul unui proces, si practic reprezinta o unitate mai fina de executie dec*t procesul. +n momentul )n care un proces este creat, )n cadrul lui exista un singur fir de executie care executa programul secvential. Acest fir poate la rndul lui sa creeze alte fire de executie , aceste fire vor rula acelasi program )n cadrul aceluiasi proces, dar fiecare fir poate executa o parte diferita a programului . +n momentul )n care )ntr"un proces este creat un nou fir de executie, acesta va partaja spatiul de adrese, descriptorii de fisiere si alte resurse cu celelalte fire din cadrul procesului respectiv. +n schimb, fiecare thread are propria stare a executiei (poate fi )n executie, blocat sau gata de executie), are propriul contor program (#() care )i indica urmatoarea instructiune ce trebuie executata, propriile valori ale registrilor si propria stiva.

$esi un thread trebuie sa se execute )n cadrul unui proces, conceptele de proces si fir de executie sunt distincte, asa cum a fost precizat de-a, procesele fiind folosite pentru a grupa resurse )mpreuna, iar firele de executie fiind entitatile planificate pentru executie pe un procesor. (eea ce aduc in plus thread"urile modelului de proces este faptul ca permit ca mai multe fluxuri de executie (care pot fi )ntr"o anumita masura independente unele de celelalte) sa aiba loc )n cadrul aceluiasi mediu al unui proces, parta-*nd resursele acestuia.

1. Avantajele firelor de executie

" (rearea.distrugerea (eliberarea de resurse) firelor de executie dureaza mai putin dec*t crearea unor noi procese )ntruc*t ele folosesc acelasi spatiu de adrese, " /impul de comutare )ntre threadurile unui proces este scazut )ntruc*t nu trebuie schimbat spatiul de adrese, " (omunicarea )ntre firele de executie ale unui proces este si ea mult mai rapida, si aceasta se datoreaza tot faptului ca ele parta-eaza spatiul de adrese, o modificare facuta de un thread fiind imediat accesibila si celorlalte thread"uri.

2. Tipuri de fire de executie


Exista 0 categorii de fire de executie : " ser &evel /hreads ( &/) " 1ernel &evel /hreads (1&/) " !ire de executie hibride

2.1 User Level Threads - model U:1 +n cazul acestor fire, 2ernelul nu este constient de existenta lor si managementul lor este realizat de aplicatii folosind o biblioteca de fire de executie. Astfel, schimbarea contextului nu necesita interventia 2ernelului, iar algoritmul de planificare depinde de aplicatie. Avantaje : " schimbarea de context nu implica 2ernelul, ceea ce implica o comutare rapida. " planificarea poate fi aleasa de aplicatie si deci se poate alege una care sa favorizeze cresterea vitezei de executie. " firele pot rula pe orice '3, deci si pe cele care nu suporta thread"uri, avand nevoie doar de biblioteca ce le implementeaza. " 2ernelul nu stie de thread"uri, deci daca un thread apeleaza ceva blocant toate thread"urile din cadrul unui proces vor fi blocate. " cele mai multe apeluri de sistem sunt blocante, iar 2ernelul face blocarea la nivel de proces. " 2ernelul nu stie sa aloce resurse dec*t proceselor, chiar daca are 4 procesoare el nu poate aloca unui proces dec*t un procesor la un moment dat si deci nu se pot executa )n paralel 5 thread"uri ale aceluiasi proces pe procesoare diferite.

Dezavantaje :

2.2

ernel Level Threads - model 1:1

6anagementul este facut )n acest caz de 2ernel si astfel nu exista nici o biblioteca de thread" uri, ci un numar de apeluri de sistem pentru utilizarea lor. 1ernelul mentine informatii de context at*t pentru procese c*t si pentru firele din cadrul proceselor, iar planificarea pentru executie se face la nivel de fir de executie. Avantaje : " daca avem mai multe procesoare putem lansa )n executie simultana mai multe thread"uri ale aceluiasi proces, blocarea unui fir nu mai )nseamna blocarea )ntregului proces. " putem scrie cod )n 2ernel care sa se bazeze pe thread"uri. 2.! "ire de executie hi#ride - model U:K Aceste fire )ncearca sa combine avanta-ele thread"urilor user"level cu cele ale thread"urilor 2ernel"level. 3 modalitate de a face acest lucru este de a utiliza fire 2ernel"level pe care sa fie multiplexate fire user"level. 1&/ sunt unitatile elementare care pot fi distribuite pe procesoare. $e regula crearea thread"urilor se face )n user space si tot aici se face planificarea si sincronizarea. Aceste thread"uri user"level sunt invizibile pentru 2ernel. 1ernel"ul stie doar de 1&/"urile pe care sunt multiplexate &/, si doar pe acestea le planifica. #rogramatorul poate schimba eventual numarul de 1&/ alocate unui proces. Dezavantaje : " comutarea de context o face 2ernelul, deci pentru fiecare schimbare de context se trece din firul de executie )n 2ernel si apoi se mai face )nca o schimbare din 2ernel )n alt fir de executie, deci viteza de comutare este mica.

Linux Threads
&inux a avut, pana la versiunea de 2ernel 5.7, o implementare unica asupra firelor de executie. #rin conceptul &inux/hreads, firele erau considerate procese (cu structuri de date asemanatoare proceselor) care parta-au resurse intre ele, avand chiar si identificatori distincti. 8ncepand din 5990, Native POSIX Threads Library a facut trecerea catre o abordare ::: in ceea ce priveste firele de executie. 'isteme de operare precum 'un3', 4et;'$ sau !ree;'$ au avut treceri similare de la o implementare la alta. Astfel, treptat s"a renuntat la conceptul de <light"weight process= sau modelul 4:6 in favoarea mult mai simplei abordari > modelul :::.

$#servatii
n proces, imediat ce a fost creat, este format dintr"un singur fir de executie, numit fir de executie principal (initial). /oate firele de executie din cadrul unui proces se vor executa in paralel. $atorita faptului ca impart aceeasi zona de date, firele de executie ale unui proces vor folosi in comun toate variabilele globale. $e aceea, se recomanda ca in programe firele de executie sa utilizeze numai variabilele locale, definite in functiile care implementeaza firul, in afara de cazurile in care se doreste parta-area explicita a unor resurse. $aca un proces format din mai multe fire de executie se termina ca urmare a primirii unui semnal, toate firele de executie ale sale se vor termina. $aca un fir de executie apeleaza functia exit( ), efectul va fi terminarea intregului proces, cu toate firele de executie din interior. 3rice functie sau apel sistem care lucreaza cu sau afecteaza procese, va avea efect asupra intregului proces, indiferent de firul de executie in care a fost apelata functia respectiva. $e exemplu, functia sleep( )va "adormi" toate firele de executie din proces, inclusiv firul de executie principal (initial), indiferent de firul care a apelat"o.

!. "unctii %$&I' pentru lucrul cu threaduri


+n ceea ce priveste thread"urile, #3'8? nu specifica daca acestea trebuie implementate )n user"space sau 2ernel"space. &inux le implementeaza )n 2ernel"space, dar nu diferentiaza thread" urile de procese dec*t prin faptul ca thread"urile parta-eaza spatiul de adrese. #entru folosirea thread"urilor )n &inux trebuie sa includem header"ul pthread.h unde se regasesc declaratiile functiilor si tipurilor de date necesare si sa utilizam biblioteca li#pthread. !.1 (rearea firelor de executie !iecare thread din s@stem se va identifica in mod unic printr"un TID (thread ID). Acesta este in mod uzual stocat intr"o variabila de tipul thread!t. !iecare thread nou creat intr"un program va executa o functie. Aceasta este de tipul void" si are un parametru formal de acelasi tip. Astfel programatorul poate transmite intr"un mod flexibil o informatie thread"ului iar threadul va putea intoarce un rezultat al executiei sale. #entru crearea unui nou fir de executie se foloseste functia thread!#reate :
#include <pthread.h> int pthread_create(pthread_t *tid, const pthread_attr_t *tattr, void* start_routine(void *), void *arg);

Apelul de s@stem se intoarce imediat si permite executia threadului initial. 4oul fir creat se va executa concurent cu firul de executie din care a fost creat. Acesta va executa codul specificat de functia start!ro$ti%e careia i se va pasa argumentul ar&. !olosind ar& se poate transmite firului de executie un pointer la o structura care sa contina toti "parametrii" necesari acestuia. $aca apelul are succes, thread!#reate )ntoarce 9 si )n variabila tid )ntoarce identificatorul threadului, un numar )ntreg pozitiv. #rin parametrul tattr se stabilesc atributele noului fir de executie. $aca transmitem valoarea NULL threadul va fi creat cu atributele implicite.

ExplicaAi ce se va )nt*mpla )n momentul )n care unul din thread"urile nou create ale unui proces apeleazB una din funcAiile din familia exec)*.
#include <pthread.h> #include <stdio.h> int a=3,b=3; void* print_xs (void* unused) { while (a--) //b-{ fputc ('x', stderr); // fputc ('x', stdout); fflush(stdout); sleep(1); } return NULL; } int main () { pthread_t thread_id; pthread_create (&thread_id, NULL, &print_xs, NULL); while (b--) { fputc ('o', stderr); // fputc ('o', stdout); fflush(stdout); sleep(1); } return 0; }

#entru vizualizarea firelor de executie, individual, se poate utiliza comanda s 'eL(, urmarind coloana &C# (&ight Ceight #rocesses). !.2 Terminarea firelor de executie

n fir de executie se termina in urmatoarele situatii: " intoarcerea valorii de catre functia apelata > ret$r% " terminarea normala cu apelul thread!e)it " terminare fortata prin anulare de catre un alt fir de executie > thread!#a%#el n fir de executie se termina normal la un apel al functiei thread!e)it :
void pthread_exit(void *retval);

$aca nu exista un astfel de apel este adaugat unul, )n mod automat, la sf*rsitul codului firului de executie. #rin parametrul retval se comunica parintelui un mesa- despre modul de terminare a copilului. Aceasta valoare va fi preluata de o functie de asteptare a thread"ului " thread!*oi%. 'tarea returnata de firele de executie poate fi preluata de catre oricare din thread"urile aceluiasi proces. $e asemenea, #3'8? ofera posibilitatea de a controla executia unui fir:
int sched_yield (void)

!unctia s#hed!yield , apelata de un fir de executia duce la eliberarea procesorului de catre fir (intreruperea executiei) si la mutarea acestuia la sfarsitul cozii de asteptare la programatorul de procese. Astfel, un alt fir de executie primeste controlul asupra procesorului. !.! Transmiterea de date catre threaduri Argumentul threadului reprezinta o metoda buna de a pasa informatii. /otusi, avand in vedere ca acesta este de tipul voidD nu putem decat sa transmitem prin acesta un pointer catre o structura de date care sa contina aceste informatii. 'e poate defini cate o astfel de structura pentru fiecare thread pentru a utiliza aceeasi functie pentru mai multe threaduri, dar cu alti parametri continuti de structura.
#include <pthread.h> #include <stdio.h> struct thread_params { char c; int count; }; void* thread_print (void* parameters) { struct thread_params* p = thread_params*) parameters; int i; for (i = 0; i < p->count; ++i) fputc (p->c, stderr); return NULL; } int main () { pthread_t thread_id[2]; struct thread_params thread_args[2]; thread_args[0].c = 'x'; thread_args[0].count = 30; pthread_create(&thread_id[0],NULL, &thread_print, &thread_args[0]); (struct thread_args[1].c = 'o'; thread_args[1].count = 20; pthread_create(&thread_id[1],NULL, &thread_print, &thread_args[1]); return 0; }

Programul de mai sus contine o greseala de conceptie. 8nformatiile din structurile locale create in main nu vor mai exista la rularea threadurilor secundare din moment ce threadul principal intoarce valoarea 9 si termina executia intregului proces. !.+ Asteptarea firelor de executie

#entru remedierea acestui nea-uns avem nevoie pentru threaduri de o constructie similara cu +ait, in cazul proceselor. &a fel ca in cazul proceselor, un parinte isi poate astepta copilul apel*nd thread!*oi%.
int pthread_join(pthread_t tid, void **thread_return);

#rimul parametru specifica identificatorul firului de executie asteptat, iar al doilea parametru specifica unde se va plasa codul )ntors de functia <copil= (cu thread!e)it sau ret$r%). +n caz de succes se )ntoarce valoarea 9, altfel se )ntoarce o valoare negativa reprezent*nd un cod de eroare. /hread"urile se )mpart )n doua categorii : " unificabile " permit unificarea cu alte threaduri care apeleaza thread!*oi%, resursele ocupate de thread 4 sunt eliberate imediat dupa terminarea threadului, ci mai sunt pastrate p*na c*nd un alt thread va executa thread!*oi%, threadurile sunt implicit unificabile, " detasabile " un thread este detasabil daca a fost creat detasabil sau daca i s"a schimbat acest atribut )n timpul executiei prin apelul thread!deta#h, nu se poate executa un thread!*oi% pe threaduri detasate. !., %reluarea starii de terminare a executiei Argumentul retval al functiei thread!*oi% poate fi utilizat fie pentru verificarea starii la terminarea executiei unui thread, fie pentru transmiterea unor date de la thread"ul care termina executia catre thread"ul apelant.
struct thread_params { char c; int i, result; }; int j; void* thread_print (void* parameters) { struct thread_params* p = thread_params*) parameters; int k; for (k = 0; k < p->i; ++k) { printf("%c",p->c); p->result=p->result+p->i; j=k; } //return (void*)&j; return (void*)&(p->result); } int main () { int * retval; pthread_t thread_id; struct thread_params thread_args; thread_args.c = 'x'; thread_args.i = 3; thread_args.result = 0; pthread_create(&thread_id,NULL, &thread_args);

(struct

thread_print,

pthread_join(thread_id,(void*)&retval); //printf("\nretval(j)=%d %d\n",*retval,thread_args.result); printf("\nretval(result)=%d %d\n",*retval,thread_args.result); return 0; } result= result=

!.- Atri#utele threadurilor Eeprezinta un mecanism pentru controlul precis al comportamentului threadurilor. #entru a stabili si modifica atributele unui fir de executie avem la dispozitie o serie de apeluri utile precum:
int pthread_attr_init(pthread_attr_t *attr); int pthread_attr_destroy(pthread_attr_t *attr); int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); int pthread_attr_getstackaddr(const pthread_attr_t *restrict attr, void **restrict stackaddr); int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr); int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, size_t *restrict stacksize); int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);

#entru informatii detaliate studiati manpage"urile


man pthread_attr(3)

#entru a crea si stabili un set de atribute speciale pentru executia threadurilor se va: :. $eclara o variabila de tipul thread!attr!t 5. Apela functia thread!attr!i%it pentru a o initializa cu valorile implicite 0. 6odifica atributele (functii distincte pentru fiecare atribut) F. #asa un pointer la variabila thread!attr!t la apelul thread!#reate G. Apela thread!attr!destroy pentru a elibera obiectul. n singur atribut poate fi utilizat pentru mai multe threaduri. Detach state > atribut care permite eliberarea resurselor unui thread care si"a incheiat executia dar care nu a fost asteptat de catre threadul <parinte= care, fie si"a incheiat executia fie nu a apucat sa apeleze thread!*oi%().
void* thread_function (void* thread_arg) { //instructiuni } int main () { pthread_attr_t attr; pthread_t thread; pthread_attr_init (&attr); pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); pthread_create (&thread, &thread_function, NULL); pthread_attr_destroy (&attr); //instructiuni // fara a fi nevoie de pthread_join return 0; } &attr,

!.. Identificarea threadurilor 8n cazul in care unul sau mai multe threaduri executa aceeasi secventa de cod, este utila stabilirea identitatii threadului executant. !unctia thread!sel(() intoarce 8$"ul(/8$) threadului apelant. #entru a compara un /8$ astfel obtinut cu un alt /8$ cunoscut se va utiliza functia thread!e,$al().
pthread_t pthread_self(void); int pthread_equal(pthread_t t1, pthread_t t2);

Acestea devin utile si atunci cand se doreste eliminarea situatiei in care un thread s"ar astepta pe sine insusi, pasand eronat functiei thread!*oi% propriul 8$.
if (!pthread_equal (pthread_self (), other_thread)); pthread_join (other_thread, NULL);

3 altB modalitate de a afla 8$"ul unui thread (sub forma unui 8$ de proces afiHat cu comanda s -eL() o reprezintB apelul de sistem &ettid(). (um, pentru acest apel de sistem, de cele mai multe ori nu avem o funcAie wrapper implementatB va trebui sB executBm direct acest apel de sistem folosind sys#all:
#include <pthread.h> #include <stdio.h> #include <unistd.h>

#include <sys/syscall.h> pid_t gettid(void) { return syscall(SYS_gettid); } void *thread_fun(void *arg) { fprintf(stderr, "child thread pid is %d\n", (int)getpid()); fprintf(stderr, "child kernel thread tid is %d\n", (int)gettid()); while(1); return NULL; } int main(void) { pthread_t thread; fprintf(stderr, "main thread pid is %d\n", (int)getpid()); fprintf(stderr, "main kernel thread tid is %d\n", (int)gettid()); pthread_create(&thread, NULL, &thread_fun, NULL); while(1); return 0; }

!./ Anularea threadurilor n fir de executie isi poate incheia sincron executia fie prin intoarcere din functia de tip thread care il defineste fie prin apelul thread!e)it(). 8n cazul in care se doreste anularea(terminarea) executiei unui thread de catre alt thread(asincron), se poate utiliza mecanismul thread #a%#ellatio%. #entru a se anula un thread se foloseste thread!#a%#el(TID). Acest lucru este de evitat avand in vedere faptul ca un thread poate fi, la un moment dat, in executia unor instructiuni ce 4 /EE; 8E intrerupte(ex. alocare de memorie). #entru aceasta, un thread poate controla starea sa relativa la posibilitatea anularii executiei sale. n thread poate fi in urmatoarele 0 stari: " i%trer$ tibil asi%#ro% > poate fi intrerupt(anulat) in orice moment, " i%trer$ tibil si%#ro% > poate fi intrerupt(anulat) numai in anumite momente(puncte de intrerupere.anulare > #a%#ellatio% oi%ts), atunci cand nu este intreruptibil, toate cererile de intrerupere(anulare) primite sunt puse intr"o coada de asteptare, " %ei%trer$ tibil > incercarile de intrerupere(anulare) sunt ignorate, aceasta stare permite controlul executiei asupra zonelor critice de cod (secvente de cod care trebuie executate fie in intregime, fie deloc). &a momentul crearii, un thread este i%trer$ tibil si%#ro%. #uncte de intrerupere > #a%#ellatio% oi%ts: " orice apel de sistem reprezinta un punct de intrerupere " se pot crea explicit puncte de intrerupere folosind apelul
void pthread_testcancel(void)

#entru a putea controla starea de intrerupere a unui fir de executie avem la dispozitie apelurile:
pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL); pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, NULL);

+n general nu este o idee foarte buna folosirea functiei thread!#a%#el pentru a termina un thread, dec*t )n cazuri exceptionale. +n cazuri normale o strategie mai buna este de a indica firului ca trebuie sa se termine si apoi sa se astepte terminarea lui.

+. Date specifice unui thread


Asa cum a fost precizat de-a, spre deosebire de procese, firele de executie din cadrul unui program parta-eaza acelasi spatiu de adrese, deci modificarea unei variabile de catre un fir este vizibila si celorlalte fire. /otusi, firele de executie au propria lor stiva, fapt ce le permite sa execute cod diferit, sa apeleze functii ce au propriile variabile locale (pastrate pe stiva). neori )nsa este util ca o variabila(globala) sa fie duplicata astfel )nc*t fiecare thread sa aiba propria lui copie separata. +n &inux este permis acest lucru prin folosirea unei zone numite thread s e#i(i# data (/'$). Iariabilele stocate )n aceasta zona sunt duplicate pentru fiecare fir de executie si fiecare fir poate sa modifice copia lui fara a afecta celelalte thread"uri. #entru a accesa variabilele stocate )n /'$ exista o serie de functii specifice. #ot fi create oric*te variabile /'$, acestea fiind de tipul void D. !iecare variabila poate fi accesata prin intermediul unei chei de tipul thread!-ey!t. #entru a crea o noua cheie, si deci o noua variabila /'$, se foloseste functia :
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));

#rimul parametru este un pointer la o variabila de tip thread!-ey!t. Aceasta cheie va fi apoi folosita de fire pentru a accesa propria copie a variabilei de tip /'$ respective. Al doilea parametru reprezinta o functie de eliberare a resurselor. $aca aici este dat un pointer la o functie, aceasta functie va fi automat apelata c*nd fiecare fir se termina si va primi ca parametru valoarea corespunzatoare cheii specifica firului respectiv. $upa crearea cheii, fiecare fir de executie poate modifica propria copie a variabilei asociate cheii folosind functia thread!sets e#i(i# :
int pthread_setspecific(pthread_key_t key, const void *pointer);

#rimul parametru reprezinta cheia, iar al doilea parametru reprezinta valoarea specifica ce trebuie stocata si care este de tipul void D. #entru a determina valoarea unei variabile de tip /'$ se foloseste functia voidD pthreadJgetspecific(pthreadJ2e@Jt 2e@) functie ce primeste ca parametru cheia corespunzatoare variabilei specifice. !unctia int pthreadJ2e@Jdelete(pthreadJ2e@Jt 2e@) dezaloca o cheie /'$. Ea nu apeleaza functia de cleanup asociata acesteia.

0xemplu T&D ' .isiere de lo& s e#i(i#e (ie#ar$i thread (#o%trol$l a##es$l$i la #o%ti%$t$l (isier$l$i oate (i reali/at ri% astrarea oi%ter$l$i la ti $l (isier i%tr-o #heie)
static pthread_key_t thread_log_key; void write_to_thread_log (const char* message) { FILE* thread_log = (FILE*) pthread_getspecific (thread_log_key); fprintf (thread_log, %s\n, message); } thread_log = fopen (thread_log_filename, w); pthread_setspecific (thread_log_key, thread_log); write_to_thread_log (Thread starting.); return NULL; }

void close_thread_log (void* thread_log) { fclose ((FILE*) thread_log); } void* thread_function (void* args) { char thread_log_filename[20]; FILE* thread_log; sprintf (thread_log_filename, thread%d.log, (int) pthread_self ());

int main () { int i; pthread_t threads[5]; pthread_key_create close_thread_log); for (i = 0; i < 5; ++i) (&thread_log_key,

pthread_create (&(threads[i]), NULL, thread_function, NULL); for (i = 0; i < 5; ++i) pthread_join (threads[i], NULL); return 0; }

E E!P"E
:.
void* thread_print1 () { printf("\nT1-%d\n",getpid()); while(1){} return NULL; } void* thread_print2 () { printf("\nT2-%d\n",getpid()); while(1){} return NULL; } int main () { pthread_t thread_id[2]; pthread_create(&thread_id[0],NULL, &thread_print1, NULL); pthread_create(&thread_id[1],NULL, &thread_print2, NULL); pthread_join(thread_id[0],NULL); pthread_join(thread_id[1],NULL); printf("\nfinal----\n"); return 0; }

5. /hreadJexit
void* char_print (void* parameters); int main () { int *p,in=3; pthread_t tid1; pthread_create &in); (&tid1, NULL, &char_print, pthread_join(tid1,(void*)&p); printf("p=%d,*p=%d\n",p,*p); return 0; } void* char_print (void* parameters) { int * x= (int*) parameters; printf("x primit ca arg este %d\n",*x); pthread_exit((void*)x); }

0. /'$
void* char_print (void* parameters); void print(); int key=10; int main () { int *p,*q,inp=1,inq=2; pthread_t tid1; pthread_t tid2; pthread_create (&tid1, NULL, &char_print, &inp); pthread_create (&tid2, NULL, &char_print, &inq); } pthread_join(tid1,(void*)&p); pthread_join(tid2,(void*)&q); printf("p=%d,*p=%d\n",p,*p); printf("q=%d,*q=%d\n",q,*q); printf("FINAL - key=%d\n",key); return 0; } void print() { printf("valoarea threadului pthread_self(),key); } void* char_print (void* parameters) { int * x= (int*) parameters; printf("x primit ca arg este %d\n",*x); key+=2; print(); pthread_exit((void*)x);

%d

este

%d\n",

F.
void* char_print (void* parameters); void print(); pthread_key_t key; int main () { int *p,*q,inp=1,inq=2; pthread_t tid1; pthread_t tid2; pthread_key_create(&key,NULL); pthread_create (&tid1, NULL, &char_print, &inp); pthread_create (&tid2, NULL, &char_print, &inq); } pthread_join(tid1,(void*)&p); pthread_join(tid2,(void*)&q); printf("p=%d,*p=%d\n",p,*p); printf("q=%d,*q=%d\n",q,*q); printf("FINAL - key=%d\n",key); void print() { int * k=(int*)pthread_getspecific(key); printf("valoarea key a threadului %d %d\n", pthread_self(), *k); } return 0; } void* char_print (void* parameters) { int * x= (int*) parameters; printf("x primit ca arg este %d\n",*x); pthread_setspecific(key,x); sleep(1); print(); pthread_exit((void*)x);

este

S-ar putea să vă placă și