Differences

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

Link to this comparison view

so:laboratoare:laborator-09 [2019/04/17 19:18]
octavian.grigorescu [BONUS]
so:laboratoare:laborator-09 [2022/05/06 13:38] (current)
costin.carabas [Exercițiul 6 - Barrier]
Line 1: Line 1:
-====== Laborator 09 - Thread-uri ​Windows ======+====== Laborator 09 - Threaduri ​Windows ======
  
  
-===== Materiale ajutătoare ===== 
- 
-  *[[http://​elf.cs.pub.ro/​so/​res/​laboratoare/​lab09-slides.pdf | lab09-slides.pdf]] 
-  *[[http://​elf.cs.pub.ro/​so/​res/​laboratoare/​lab09-refcard.pdf | lab09-refcard.pdf]] 
  
 ==== Nice to read ==== ==== Nice to read ====
  
   * WSP4 - Chapter 7, Threads and Scheduling   * WSP4 - Chapter 7, Threads and Scheduling
-===== Crearea firelor de execuție ======+==== Crearea firelor de execuție ======
  
 Pentru a lansa un nou fir de execuție, există funcțiile [[http://​msdn.microsoft.com/​en-us/​library/​ms682453%28VS.85%29.aspx|CreateThread]] și [[http://​msdn.microsoft.com/​en-us/​library/​ms682437%28v=VS.85%29.aspx|CreateRemoteThread]] (a doua fiind folosită pentru a crea un fir de execuție în cadrul altui proces decât cel curent). [[https://​www.codeproject.com/​Articles/​86215/​Remote-Threads-Basics-Part-1|Aici]] găsiți mai multe informații despre remote threads. Pentru a lansa un nou fir de execuție, există funcțiile [[http://​msdn.microsoft.com/​en-us/​library/​ms682453%28VS.85%29.aspx|CreateThread]] și [[http://​msdn.microsoft.com/​en-us/​library/​ms682437%28v=VS.85%29.aspx|CreateRemoteThread]] (a doua fiind folosită pentru a crea un fir de execuție în cadrul altui proces decât cel curent). [[https://​www.codeproject.com/​Articles/​86215/​Remote-Threads-Basics-Part-1|Aici]] găsiți mai multe informații despre remote threads.
Line 344: Line 340:
 Win32 API pune la dispoziție un mecanism de acces sincronizat la variabile partajate între fire de execuție prin intermediul funcțiilor **interlocked** ([[http://​msdn.microsoft.com/​en-us/​library/​ms684122%28VS.85%29.aspx|Interlocked Variable Access]]), precum și operații atomice de inserare și ștergere în liste simplu înlănțuite ([[http://​msdn.microsoft.com/​en-us/​library/​ms684121%28VS.85%29.aspx|Interlocked Singly Linked Lists]]). Win32 API pune la dispoziție un mecanism de acces sincronizat la variabile partajate între fire de execuție prin intermediul funcțiilor **interlocked** ([[http://​msdn.microsoft.com/​en-us/​library/​ms684122%28VS.85%29.aspx|Interlocked Variable Access]]), precum și operații atomice de inserare și ștergere în liste simplu înlănțuite ([[http://​msdn.microsoft.com/​en-us/​library/​ms684121%28VS.85%29.aspx|Interlocked Singly Linked Lists]]).
 ==== Mutex Win32 ==== ==== Mutex Win32 ====
 +=== Crearea și deschiderea ===
  
-Pe scurt:+Sunt operații prin care se  obține un ''​HANDLE''​ al unui obiect de tip mutex. Este necesar doar un singur apel, fie el de creare sau de deschidere (se presupune ca alt proces a creat deja mutex-ul). 
 + 
 +Pentru a crea un mutex se folosește funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms682411(VS.85).aspx|CreateMutex]] cu sintaxa : 
 + 
 +<columns 100% 50% ->
  
 <code c> <code c>
-/* creează un mutex */ +HANDLE ​ CreateMutex( 
-HANDLE CreateMutex( +     ​LPSECURITY_ATTRIBUTES ​lpAttributes
- LPSECURITY_ATTRIBUTES ​lpMutexAttributes,  +     ​BOOL bInitialOwner,​ 
- BOOL bInitialOwner,​  +     ​LPCTSTR lpName
- LPCTSTR lpName+
 ); );
-  +</​code>​ 
-/* deschide un mutex (identificat prin nume) */ + 
-HANDLE OpenMutex( +<​newcolumn>​ 
- DWORD dwDesiredAccess,  + 
- BOOL bInheritHandle +<code c> 
- LPCTSTR lpName +hMutex = CreateMutex( 
-); +     ​NULL,  ​/* default security attributes ​*/ 
-  +     FALSE/* initially not owned */ 
-/* eliberează un mutex ocupat ​*/ +     NULL /* unnamed ​mutex */
-BOOL ReleaseMutex( +
- HANDLE hMutex+
 ); );
 </​code>​ </​code>​
  
-Mai multe informaţii puteţgăsi în secţiunea dedicată  [[so:laboratoare-2013:laborator-05#​Mutex-uri|comunicației inter-proces]].+</​columns>​ 
 + 
 +Pentru a deschide un mutex deja existent este definită funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms684315(VS.85).aspx|OpenMutex]] cu sintaxa : 
 + 
 +<columns 100% 50% -> 
 + 
 +<code c> 
 + ​HANDLE ​ OpenMutex( 
 +     ​DWORD ​ dwDesiredAccess,​ 
 +     ​BOOL ​ bInheritHandle,​ 
 +     ​LPCTSTR ​ lpName 
 + ); 
 +</​code>​ 
 + 
 +<​newcolumn>​ 
 + 
 +<code c> 
 +hMutex = OpenMutex( 
 +        MUTEX_ALL_ACCESS, ​ /* request full access */ 
 +        FALSE, ​            /* handle not inheritable */ 
 +        "​MyMutex" ​         /* object name */ 
 +);         
 +</​code>​ 
 +</​columns>​ 
 + 
 +=== Obținerea === 
 + 
 +Obținerea unui mutex se realizează folosind una din funcțiile de așteptare tratate anterior. 
 + 
 +Încercarea de acaparare a unui mutex presupune următorii pași
 +  * se verifică dacă mutex-ul este disponibil 
 +  * dacă da, îl pot acapara și devine indisponibil,​ și funcția întoarce succes 
 +  * dacă nu, aștept să devină disponibil, după care îl acaparez, și funcția întoarce succes 
 +  * la time-out funcția întoarce eroare (atenție: e posibil să nu existe time-out) 
 + 
 +Încercarea de obținere se poate face cu sau fară timp de expirare (time-out) în funcție de parametrii dați funcțiilor de așteptare. Cea mai des folosită funcție de așteptare este [[http://​msdn.microsoft.com/​en-us/​library/​ms687032%28VS.85%29.aspx|WaitForSingleObject]]. 
 + 
 +=== Cedarea === 
 + 
 +Folosind funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms685066(VS.85).aspx|ReleaseMutex]] se cedează posesia mutex-ului, el devenind iar disponibil. Funcția are următoarea sintaxă : 
 + 
 +<code c> 
 + ​BOOL ​ ReleaseMutex( 
 +     ​HANDLE ​ hMutex 
 + ); 
 +</​code>​ 
 + 
 +Funcția va eșua dacă procesul nu deține mutex-ul. 
 + 
 +**Atenție!** pentru a putea folosi această funcție ''​HANDLE''​-ul trebuie să aibă cel puțin dreptul de acces ''​MUTEX_MODIFY_STATE''​. 
 +=== Distrugerea === 
 + 
 +Operația de **distrugere** a unui mutex este aceeași ca pentru orice ''​HANDLE''​. Se folosește funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms724211%28VS.85%29.aspx|CloseHandle]]. După ce toate ''​HANDLE''​-urile unui mutex au fost închise, mutexul este distrus și resursele ocupate de acesta eliberate. 
 + 
 +**Atenție!** La terminarea execuției unui program toate ''​HANDLE''​-urile folosite de acesta sunt automat închise. Deci, spre deosebire de semafoarele IPC din Linux, este imposibil ca un mutex (sau semafor) în Windows să mai existe în sistem după ce programele care l-au folosit/​creat s-au terminat.
  
 ==== Semafor Win32 ==== ==== Semafor Win32 ====
 +Un semafor este un obiect de sincronizare care are intern un contor ce ia doar valori pozitive. Atât timp cât semaforul (contorul) are valori strict pozitive el este considerat disponibil (//​signaled//​). Când valoarea semaforului a ajuns la zero el devine indisponibil (//​nonsignaled//​) și următoarea încercare de decrementare va duce la o blocare a threadului/​procesului de pe care s-a făcut apelul până când semaforul devine disponibil.
  
-Avem următoarele funcţii:+Operația de decrementare se realizează doar cu o singură unitate (la fel ca în API-ul POSIX), în timp ce incrementarea se poate realiza cu orice valoare în limita maximă. 
 + 
 +=== Crearea și deschiderea === 
 + 
 +Funcția de creare a semafoarelor este [[http://​msdn.microsoft.com/​en-us/​library/​ms682438(VS.85).aspx|CreateSemaphore]] și are sintaxa ​:
  
 <code c> <code c>
-/* creează un semafor */ + HANDLE ​ CreateSemaphore( 
-HANDLE CreateSemaphore( +     ​LPSECURITY_ATTRIBUTES ​ ​lpSemaphoreAttributes
- LPSECURITY_ATTRIBUTES ​semattr,  +     ​LONG  ​lInitialCount
- LONG initial_count+     ​LONG  ​lMaximumCount
- LONG maximum_count,  +     ​LPCTSTR ​ ​lpNAME 
- LPCTSTR ​name + );
-); +
-  +
-/* deschide un semafor existent */ +
-HANDLE OpenSemaphore( +
- DWORD dwDesiredAccess,​  +
- BOOL bInheritHandle,​  +
- LPCTSTR name +
-); +
-  +
-/* incrementeare contor semafor cu '​lReleaseCount'​ */ +
-BOOL ReleaseSemaphore( +
- HANDLE hSemaphore,  +
- LONG lReleaseCount,​  +
- LPLONG lpPreviousCount +
-);+
 </​code>​ </​code>​
 +
 +Această funcție se poate folosi și pentru deschiderea unui semafor deja existent. Alternativ, pentru a folosi un semafor deja existent, este necesar obținerea ''​HANDLE''​-ului semaforului,​ operație ce se realizează folosind funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms684326.aspx|OpenSemaphore]] cu următoarea sintaxă :
 +
 +<code c>
 + ​HANDLE ​ OpenSemaphore(
 +     ​DWORD ​ dwDesiredAccess,​
 +     ​BOOL ​ bInheritHandle,​
 +     ​LPCTSTR ​ lpNAME
 + );
 +</​code>​
 +
 +=== Decrementarea (așteptarea) ===
 +
 +Operația de decrementare a semaforului cu sau fără așteptare se realizează folosind una din funcțiile de așteptare. Cea mai des folosită este funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms687032(VS.85).aspx|WaitForSingleObject]].
 +
 +===  Incrementarea ===
 +
 +Incrementarea semaforului se realizează folosind funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms685071(VS.85).aspx|ReleaseSemaphore]] cu sintaxa :
 +
 +<code c>
 + ​BOOL ​ ReleaseSemaphore(
 +     ​HANDLE ​ hSemaphore,
 +     ​LONG ​ lReleaseCount,​
 +     ​LPLONG ​ lpPreviousCount
 + );
 +</​code>​
 +
 +=== Distrugerea ===
 +
 +Operația de distrugere a unui semafor este similară cu cea de distrugere a unui mutex. Se folosește funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms724211%28VS.85%29.aspx|CloseHandle]]. După ce toate ''​HANDLE''​-urile unui semafor au fost închise, semaforul este distrus și resursele ocupate de acesta eliberate.
 +
 ==== Secțiune critică ==== ==== Secțiune critică ====
  
Line 616: Line 689:
 ==== Adăugarea de taskuri la thread pool ==== ==== Adăugarea de taskuri la thread pool ====
  
-=== Așteptarea unei operații de intrare/​ieșire asincrone ​=== +<​spoiler>​ 
 +**Așteptarea unei operații de intrare/​ieșire asincrone**\\
 Pentru a adăuga la //thread pool// un task care se va executa la finalul unei operații de intrare/​ieșire asincrone pe un anumit //file handle//, se va apela funcția: Pentru a adăuga la //thread pool// un task care se va executa la finalul unei operații de intrare/​ieșire asincrone pe un anumit //file handle//, se va apela funcția:
  
Line 640: Line 713:
 </​code>​ </​code>​
  
-=== Adăugarea unui task pentru execuție imediată ​=== +**Adăugarea unui task pentru execuție imediată**\\
 Pentru a adăuga la //thread pool// un task care să fie executat imediat se va apela funcția: Pentru a adăuga la //thread pool// un task care să fie executat imediat se va apela funcția:
  
Line 655: Line 727:
 ); );
 </​code>​ </​code>​
 +</​spoiler>​
 ==== Timer Queues ==== ==== Timer Queues ====
  
Line 731: Line 804:
 </​code>​ </​code>​
 ==== Registered Wait Functions ==== ==== Registered Wait Functions ====
 +<​spoiler>​
 Funcțiile de așteptare înregistrate sunt funcții de așteptare executate de un fir de execuție din //thread pool//. În momentul în care obiectul de sincronizare după care se așteaptă trece în starea //​signaled//,​ se va executa rutina //​callback//​ asociată funcției de așteptare înregistrate,​ de un fir de execuție din //thread pool//. În mod implicit, funcțiile de așteptare înregistrate se **rearmează automat** și rutinele //​callback//​ sunt executate de fiecare dată când obiectul de sincronizare după care se așteaptă trece în starea **//​signaled//​**,​ sau intervalul de timeout **expiră**. Acest lucru se repetă până când înregistrarea funcției de așteptare este anulată. Se poate seta, însă, ca funcția de așteptare înregistrată să se execute **o singură dată**. Funcțiile de așteptare înregistrate sunt funcții de așteptare executate de un fir de execuție din //thread pool//. În momentul în care obiectul de sincronizare după care se așteaptă trece în starea //​signaled//,​ se va executa rutina //​callback//​ asociată funcției de așteptare înregistrate,​ de un fir de execuție din //thread pool//. În mod implicit, funcțiile de așteptare înregistrate se **rearmează automat** și rutinele //​callback//​ sunt executate de fiecare dată când obiectul de sincronizare după care se așteaptă trece în starea **//​signaled//​**,​ sau intervalul de timeout **expiră**. Acest lucru se repetă până când înregistrarea funcției de așteptare este anulată. Se poate seta, însă, ca funcția de așteptare înregistrată să se execute **o singură dată**.
  
-=== Înregistrarea unei funcții de așteptare ​=== +**Înregistrarea unei funcții de așteptare**\\
 Pentru înregistrarea în //thread pool// a unei funcții de așteptare se va apela funcția: Pentru înregistrarea în //thread pool// a unei funcții de așteptare se va apela funcția:
  
Line 762: Line 834:
 Prin intermediul parametrului ''​dwFlags''​ se pot transmite caracteristici ale firului de execuție care va executa rutina ''​Callback'',​ precum și dacă funcția de așteptare trebuie să se execute doar o singură dată. Funcția va întoarce, prin parametrul ''​phNewWaitObject'',​ un //handle// ce va fi folosit pentru deînregistrarea funcției de așteptare. Prin intermediul parametrului ''​dwFlags''​ se pot transmite caracteristici ale firului de execuție care va executa rutina ''​Callback'',​ precum și dacă funcția de așteptare trebuie să se execute doar o singură dată. Funcția va întoarce, prin parametrul ''​phNewWaitObject'',​ un //handle// ce va fi folosit pentru deînregistrarea funcției de așteptare.
  
-=== Deînregistrarea unei funcții de așteptare ​=== +**Deînregistrarea unei funcții de așteptare**\\
 Pentru a anula înregistrarea unei funcții de așteptare se va apela una dintre funcțiile: Pentru a anula înregistrarea unei funcții de așteptare se va apela una dintre funcțiile:
  
Line 774: Line 845:
  
 Funcția ''​UnregisterWaitEx''​ va semnaliza //​event//​-ul ''​CompletionEvent''​ în cazul în care se termină cu succes și rutina de //​callback//​ s-a terminat cu succes. Dacă valoarea lui ''​CompletionEvent''​ nu este ''​NULL'',​ atunci funcția va aștepta finalizarea operației de așteptare și terminarea rutinei asociate. ​ Funcția ''​UnregisterWaitEx''​ va semnaliza //​event//​-ul ''​CompletionEvent''​ în cazul în care se termină cu succes și rutina de //​callback//​ s-a terminat cu succes. Dacă valoarea lui ''​CompletionEvent''​ nu este ''​NULL'',​ atunci funcția va aștepta finalizarea operației de așteptare și terminarea rutinei asociate. ​
 +</​spoiler>​
 +====== Sumar ======
 +^  Operație ​ ^  POSIX  ^  Windows ​ ^
 +|  Crearea unui fir de execuție ​ |  ''​pthread_create'' ​ |  [[#​crearea_firelor_de_executie|CreateThread]] ​ |
 +|  Așteptarea unui fir de execuție ​ |  ''​pthread_join'' ​ |  [[#​asteptarea_firelor_de_executie|WaitForSingleObject]] ​ |
 +|  Crearea unui mutex  |  ''​pthread_mutex_init'' ​ |  [[#​crearea_si_deschiderea|CreateMutex]] ​ |
 +|  Obținerea unui mutex  |  ''​pthread_mutex_lock'' ​ |  [[#​obtinerea|WaitForSingleObject]] ​ |
 +|  Cedarea unui mutex  |  ''​pthread_mutex_unlock'' ​ |  [[#​cedarea|ReleaseMutex]] ​ |
 +|  Distrugerea unui mutex  |  ''​pthread_mutex_destroy'' ​ |  [[#​distrugerea|CloseHandle]] ​ |
 +|  Crearea unui semafor ​ |  ''​sem_init''/''​sem_open'' ​ |  [[#​crearea_si_deschiderea1|CreateSemaphore]] ​ |
 +|  Decrementarea unui semafor ​ |  ''​sem_wait'' ​ |  [[#​decrementarea_asteptarea|WaitForSingleObject]] ​ |
 +|  Incrementarea unui semafor ​ |  ''​sem_post'' ​ |  [[#​incrementarea|ReleaseSemaphore]] ​ |
 +|  Distrugerea unui semafor ​ |  ''​sem_destroy''/''​sem_close'' ​ |  [[#​distrugerea1|CloseHandle]] ​ |
 +|  Thread Local Storage ​ |  [[https://​gcc.gnu.org/​onlinedocs/​gcc/​Thread-Local.html|__thread]],​ [[https://​linux.die.net/​man/​3/​pthread_key_create|pthread_key_create]],​ [[https://​linux.die.net/​man/​3/​pthread_setspecific|pthread_setspecific]],​ [[https://​linux.die.net/​man/​3/​pthread_getspecific|pthread_getspecific]],​ [[https://​linux.die.net/​man/​3/​pthread_key_delete|pthread_key_delete]] ​ |  [[#​thread_local_storage|TlsAlloc,​ TlsSetValue,​ TlsGetValue, ​ TlsFree]] ​ |
  
-====== Exerciții de laborator ====== 
  
-===== Exercițiul 0 - Joc interactiv (2p) =====+====== Exerciții ======
  
-  * Detalii desfășurare [[http://ocw.cs.pub.ro/courses/so/meta/notare#​joc_interactiv|joc]].+<note important>​ 
 +În cadrul laboratoarelor vom folosi repository-ul de git al materiei SO - https://github.com/​systems-cs-pub-ro/so. Va trebui sa clonați repository-ul pe masinile virtuale folosind comanda: ''​git clone https://​github.com/​systems-cs-pub-ro/so''​. Dacă doriți să descărcați repositoryul în altă locație, folosiți comanda ''​git clone https://github.com/​systems-cs-pub-ro/​so ${target}''​.
  
-===== Windows (9p) =====+Pentru a actualiza repository-ul,​ folosiți comanda ''​git pull origin master''​ din interiorul directorului în care se află repository-ul. Recomandarea este să îl actualizați cât mai frecvent, înainte să începeți lucrul, pentru a vă asigura că aveți versiunea cea mai recentă. În cazul în care gitul detectează conflicte la nivelul vreunui fişier, folosiți următoarele comenzi pentru a vă păstra modificările:​ 
 +<​code>​ 
 +git stash 
 +git pull origin master 
 +git stash pop 
 +</​code>​ 
 + 
 +Pentru mai multe informații despre folosirea utilitarului git, urmați ghidul de la https://​gitimmersion.com. 
 +</​note>​
  
-În rezolvarea laboratorului folosiți arhiva de sarcini [[http://​elf.cs.pub.ro/​so/​res/​laboratoare/​lab09-tasks.zip | lab09-tasks.zip]]+===== Windows =====
  
 <note tip> Pentru a deschide proiectul Visual Studio conținând exercițiile,​ deschideți fișierul lab09.sln. </​note>​ <note tip> Pentru a deschide proiectul Visual Studio conținând exercițiile,​ deschideți fișierul lab09.sln. </​note>​
    
-==== Exercițiul 1 - Threading și priorități ​(1p) ====+==== Exercițiul 1 - Threading și priorități ====
  
 Încărcați proiectul ''​1-threading''​ și setați-l ca //StartUp Project//. Compilați și rulați programul. Aflați câte fire de execuție creează în total. Încărcați proiectul ''​1-threading''​ și setați-l ca //StartUp Project//. Compilați și rulați programul. Aflați câte fire de execuție creează în total.
Line 798: Line 891:
  
 <note tip>​Dacă setați ca prioritate ''​real-time''​ și comentați linia cu ''​Sleep''​ din bucla ''​while'',​ cel mai probabil vi se va bloca mașina virtuală. Acest lucru s-ar întâmpla pentru că ar exista tot timpul un thread cu prioritate mai mare ca cele pentru interfața grafică, de exemplu, gata să ruleze pe procesor. Vezi și [[https://​en.wikipedia.org/​wiki/​Starvation_(computer_science)| link]].</​note>​ <note tip>​Dacă setați ca prioritate ''​real-time''​ și comentați linia cu ''​Sleep''​ din bucla ''​while'',​ cel mai probabil vi se va bloca mașina virtuală. Acest lucru s-ar întâmpla pentru că ar exista tot timpul un thread cu prioritate mai mare ca cele pentru interfața grafică, de exemplu, gata să ruleze pe procesor. Vezi și [[https://​en.wikipedia.org/​wiki/​Starvation_(computer_science)| link]].</​note>​
-==== Exercițiul 2 - Thread debugging ​(1p) ====+==== Exercițiul 2 - Thread debugging ====
  
 Deschideți sursa ''​2-debug.c''​ din proiectul ''​2-debug''​ și completați funcția ''​StartThread''​ pentru a implementa crearea unui fir de execuție (urmăriți în cod secțiunea marcată cu //TODO//). Deschideți sursa ''​2-debug.c''​ din proiectul ''​2-debug''​ și completați funcția ''​StartThread''​ pentru a implementa crearea unui fir de execuție (urmăriți în cod secțiunea marcată cu //TODO//).
Line 807: Line 900:
 Soluția nu implică comentarea funcției ''​Sleep''​. Inspectați funcțiile ''​MakeCake'',​ ''​MakeTiramisu''​ și ''​MakeMarshmallows''​ și observați ordinea în care se face ''​WaitForSingleObject''​ pe ingrediente (semafoare). Amintiți-vă din laboratorul precedent care era problema de la [[http://​ocw.cs.pub.ro/​courses/​so/​laboratoare/​laborator-08#​exercitiul_5_-_blocked_15p|Exercițiul 5]]. Soluția nu implică comentarea funcției ''​Sleep''​. Inspectați funcțiile ''​MakeCake'',​ ''​MakeTiramisu''​ și ''​MakeMarshmallows''​ și observați ordinea în care se face ''​WaitForSingleObject''​ pe ingrediente (semafoare). Amintiți-vă din laboratorul precedent care era problema de la [[http://​ocw.cs.pub.ro/​courses/​so/​laboratoare/​laborator-08#​exercitiul_5_-_blocked_15p|Exercițiul 5]].
 </​note>​ </​note>​
-==== Exercițiul 3 - Interlocked ​(2p) ====+==== Exercițiul 3 - Interlocked ====
  
 În cadrul acestui exercițiu dorim să testăm diverse tipuri de incrementări atomice ale unei variabile, comparându-le timpul de execuție. Deschideți sursa ''​interlocked.c''​ din proiectul ''​3-interlocked''​. Programul crează ''​NO_THREADS''​ fire de execuție, care incrementează circular o variabilă (când se ajunge la o limită se resetează la 0). În cadrul acestui exercițiu dorim să testăm diverse tipuri de incrementări atomice ale unei variabile, comparându-le timpul de execuție. Deschideți sursa ''​interlocked.c''​ din proiectul ''​3-interlocked''​. Programul crează ''​NO_THREADS''​ fire de execuție, care incrementează circular o variabilă (când se ajunge la o limită se resetează la 0).
  
-Asigurați accesul exclusiv la variabila incrementată folosind [[#operatii atomice cu variabile partajate interlocked variable access | Interlocked Variables]] deoarece mecanismul e mai rapid decât o incrementare normală protejată cu ''​Mutex''​ sau ''​CRITICAL_SECTION''​ (folosiți funcția ''​InterlockedCompareExchange''​). Incrementarea circulară se va face în funcția ''​thread_function''​ (urmăriți comentariile cu // TODO 1 //). Veți avea nevoie de două operații interlocked (''​InterlockedIncrement''​ și ''​InterlockedCompareExchange''​).+Asigurați accesul exclusiv la variabila incrementată folosind [[#operatii atomice cu variabile partajate interlocked variable access | Interlocked Variables]] deoarece mecanismul e mai rapid decât o incrementare normală protejată cu ''​Mutex''​ sau ''​CRITICAL_SECTION''​ (folosiți funcția ''​InterlockedCompareExchange''​). Incrementarea circulară se va face în funcția ''​thread_function''​ (urmăriți comentariile cu //TODO 1//). Veți avea nevoie de două operații interlocked (''​InterlockedIncrement''​ și ''​InterlockedCompareExchange''​).
  
 Identificați o problemă cu folosirea ''​Interlocked Operations''​ pentru a incrementa circular o variabilă. Identificați o problemă cu folosirea ''​Interlocked Operations''​ pentru a incrementa circular o variabilă.
Line 820: Line 913:
  
 Comparați timpul de execuție al programului precedent în cazul în care se folosește un mutex care să sincronizeze accesul la variabila ''​count'',​ completând funcția ''​thread_function_mutex''​ ( // TODO 2 // ). Nu uitați să modificați și parametrul funcției ''​CreateThread''​ din funcția ''​main''​. Comparați timpul de execuție al programului precedent în cazul în care se folosește un mutex care să sincronizeze accesul la variabila ''​count'',​ completând funcția ''​thread_function_mutex''​ ( // TODO 2 // ). Nu uitați să modificați și parametrul funcției ''​CreateThread''​ din funcția ''​main''​.
-==== Exercițiul 4 - TLS (1p) ====+==== Exercițiul 4 - TLS ====
  
 Dorim să simulăm o implementare a funcției ''​perror''​. Pentru aceasta vom avea variabila globală ''​myErrno'',​ dar cu valori specifice (diferite) pentru fiecare fir de execuție. Deschideți sursa ''​tls.c''​ din proiectul ''​4-tls''​ și urmăriți comentariile marcate cu //TODO// (revedeți secțiunea despre [[#​thread_local_storage | TLS]]). Dorim să simulăm o implementare a funcției ''​perror''​. Pentru aceasta vom avea variabila globală ''​myErrno'',​ dar cu valori specifice (diferite) pentru fiecare fir de execuție. Deschideți sursa ''​tls.c''​ din proiectul ''​4-tls''​ și urmăriți comentariile marcate cu //TODO// (revedeți secțiunea despre [[#​thread_local_storage | TLS]]).
  
-==== Exercițiul 5 - TimerQueue ​(2p) ====+==== Exercițiul 5 - TimerQueue ====
  
 Deschideți sursa ''​timer.c''​ din proiectul ''​5-timer''​. Creați un ''​Timer-Queue Timer'',​ a cărui rutină //​callback//​ să fie declanșată de exact 3 ori, o dată la fiecare secundă. După 3 declanșări se va dezactiva timerul și se vor distruge toate resursele create. Trebuie să sincronizați rutina //​timer//​-ului cu funcția ''​main''​ care va dezactiva timer-ul; pentru aceasta puteți folosi orice mecanism de semnalizare:​ semafor, event Deschideți sursa ''​timer.c''​ din proiectul ''​5-timer''​. Creați un ''​Timer-Queue Timer'',​ a cărui rutină //​callback//​ să fie declanșată de exact 3 ori, o dată la fiecare secundă. După 3 declanșări se va dezactiva timerul și se vor distruge toate resursele create. Trebuie să sincronizați rutina //​timer//​-ului cu funcția ''​main''​ care va dezactiva timer-ul; pentru aceasta puteți folosi orice mecanism de semnalizare:​ semafor, event
Line 833: Line 926:
 Deschideți sursa ''​barrier.c''​ din proiectul ''​6-Barrier''​. Implementați o barieră reutilizabilă folosind un mutex și o variabilă de tip eveniment. Completați funcțiile de lucru cu bariera pentru a obține funcționalitatea dorită (comentariile marcate cu //TODO//). Deschideți sursa ''​barrier.c''​ din proiectul ''​6-Barrier''​. Implementați o barieră reutilizabilă folosind un mutex și o variabilă de tip eveniment. Completați funcțiile de lucru cu bariera pentru a obține funcționalitatea dorită (comentariile marcate cu //TODO//).
  
-Pentru a putea semnaliza un obiect și a aștepta la un alt obiect de sincronizare în același timp, puteți folosi funcția [[http://msdn.microsoft.com/​en-us/​library/ms686293%28VS.85%29.aspx| SignalObjectAndWait ]]. De asemenea, revedeți secțiunile despre lucrul cu [[#mutex win32 | mutex-uri]] și [[#​evenimente | evenimente]].+Pentru a putea semnaliza un obiect și a aștepta la un alt obiect de sincronizare în același timp, puteți folosi funcția [[https://docs.microsoft.com/​en-us/​windows/desktop/​api/​synchapi/​nf-synchapi-signalobjectandwait| SignalObjectAndWait ]]. De asemenea, revedeți secțiunile despre lucrul cu [[#mutex win32 | mutex-uri]] și [[#​evenimente | evenimente]].
  
  
Line 839: Line 932:
 typedef struct { typedef struct {
  HANDLE hGuard;​  ​   /* mutex to protect internal variable access */  HANDLE hGuard;​  ​   /* mutex to protect internal variable access */
- HANDLE hEvent;​  ​   /* auto-resetable event */+ HANDLE hEvent;​  ​   /* manual ​resetable event */
  DWORD dwCount;​  ​   /* number of threads to have reached the barrier */  DWORD dwCount;​  ​   /* number of threads to have reached the barrier */
  DWORD dwThreshold; ​ /* barrier limit */  DWORD dwThreshold; ​ /* barrier limit */
Line 881: Line 974:
     * Implementați vizualizarea unui timer; spre exemplu, un fir de execuție care alternează două operații: draw și sleep. (desenează,​ se oprește, desenează, iar se oprește și tot așa; intervalul unei operații poate fi același)     * Implementați vizualizarea unui timer; spre exemplu, un fir de execuție care alternează două operații: draw și sleep. (desenează,​ se oprește, desenează, iar se oprește și tot așa; intervalul unei operații poate fi același)
 */ */
-===== Soluții ===== 
- 
- ​[[http://​elf.cs.pub.ro/​so/​res/​laboratoare/​lab09-sol.zip | Soluţii laborator 9]] 
  
-~~NOCACHE~~ 
so/laboratoare/laborator-09.1555517916.txt.gz · Last modified: 2019/04/17 19:18 by octavian.grigorescu
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