Differences

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

Link to this comparison view

so:laboratoare:laborator-09 [2021/05/02 17:28]
teodor_stefan.dutu [Cedarea]
so:laboratoare:laborator-09 [2022/05/06 13:38] (current)
costin.carabas [Exercițiul 6 - Barrier]
Line 416: Line 416:
  
 **Atenție!** pentru a putea folosi această funcție ''​HANDLE''​-ul trebuie să aibă cel puțin dreptul de acces ''​MUTEX_MODIFY_STATE''​. **Atenție!** pentru a putea folosi această funcție ''​HANDLE''​-ul trebuie să aibă cel puțin dreptul de acces ''​MUTEX_MODIFY_STATE''​.
-==== Distrugerea ​====+=== 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. 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.
Line 422: Line 422:
 **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. **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. 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.
  
 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ă. 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 ​====+=== 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 : Funcția de creare a semafoarelor este [[http://​msdn.microsoft.com/​en-us/​library/​ms682438(VS.85).aspx|CreateSemaphore]] și are sintaxa :
Line 450: Line 450:
 </​code>​ </​code>​
  
-==== Decrementarea (așteptarea) ​ ====+=== 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]]. 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 ===
  
 Incrementarea semaforului se realizează folosind funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms685071(VS.85).aspx|ReleaseSemaphore]] cu sintaxa : Incrementarea semaforului se realizează folosind funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms685071(VS.85).aspx|ReleaseSemaphore]] cu sintaxa :
Line 466: Line 466:
 </​code>​ </​code>​
  
-==== Distrugerea ​====+=== 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. 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.
Line 689: 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 713: 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 728: Line 727:
 ); );
 </​code>​ </​code>​
 +</​spoiler>​
 ==== Timer Queues ==== ==== Timer Queues ====
  
Line 804: 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 835: 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 847: 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ții ======
-===== Windows ​=====+
  
 <note important>​ <note important>​
Line 865: Line 875:
 Pentru mai multe informații despre folosirea utilitarului git, urmați ghidul de la https://​gitimmersion.com. Pentru mai multe informații despre folosirea utilitarului git, urmați ghidul de la https://​gitimmersion.com.
 </​note>​ </​note>​
 +
 +===== 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>​
Line 892: Line 904:
 Î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 920: 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 962: 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.1619965701.txt.gz · Last modified: 2021/05/02 17:28 by teodor_stefan.dutu
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