Differences

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

Link to this comparison view

so:laboratoare:laborator-09 [2017/05/03 13:43]
theodor.stoican [Alte funcții utile]
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).+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 144: Line 140:
  
 Funcția întoarce ''​TRUE''​ dacă procesorul este cedat unui alt fir de execuție și ''​FALSE''​ dacă nu există alte fire gata de execuție. Funcția întoarce ''​TRUE''​ dacă procesorul este cedat unui alt fir de execuție și ''​FALSE''​ dacă nu există alte fire gata de execuție.
 +
 +Detalii despre stările și tranzițiile unui Thread găsiți [[https://​ocw.cs.pub.ro/​courses/​_media/​so/​laboratoare/​thread-states-and-transitions.png|aici]].
  
 </​spoiler>​ </​spoiler>​
Line 283: Line 281:
 ===== Fibre de execuție ===== ===== Fibre de execuție =====
  
-Windows pune la dispoziție și o implementare de User-space Threads, numite **fibre**. Kernel-ul planifică un singur Kernel Level Thread (KLT) asociat cu un set de fibre, iar fibrele colaborează pentru a partaja timpul de procesor oferit acestuia. Deși viteza de execuție este mai bună (pentru context-switch,​ nu mai este necesară interacțiunea cu kernel-ul), programele scrise folosind fibre pot deveni complexe. Mai multe informații puteți găsi în cadrul [[so:​laboratoare-2013:​resurse:​threaduri_extra#​fibre de executie|secțiunii suplimentare dedicate]].+Windows pune la dispoziție și o implementare de User-space Threads, numite **fibre**. Kernel-ul planifică un singur Kernel Level Thread (KLT) asociat cu un set de fibre, iar fibrele colaborează pentru a partaja timpul de procesor oferit acestuia. Deși viteza de execuție este mai bună (pentru context-switch,​ nu mai este necesară interacțiunea cu kernel-ul), programele scrise folosind fibre pot deveni complexe. Mai multe informații puteți găsi în cadrul [[so:​laboratoare-2013:​resurse:​threaduri_extra#​fibre de executie|secțiunii suplimentare dedicate]], iar [[https://​ocw.cs.pub.ro/​courses/​_media/​so/​laboratoare/​windows-fibers.png|aici]] puteți vedea control flow-ul într-o reprezentare grafică.
  
 ===== Securitate și drepturi de acces ===== ===== Securitate și drepturi de acces =====
Line 342: 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 401: Line 476:
 Operațiile care se pot efectua asupra unei secțiuni critice sunt: intrarea, intrarea neblocantă,​ ieșirea din secțiunea critică, inițializarea și distrugerea. Operațiile care se pot efectua asupra unei secțiuni critice sunt: intrarea, intrarea neblocantă,​ ieșirea din secțiunea critică, inițializarea și distrugerea.
  
-Pentru serializarea accesului la o secțiune critică, fiecare fir de execuție va trebui să intre într-un obiect ''​CriticalSection''​ la începutul secțiunii și să-l părăsească la sfârșitul ei. În acest fel, dacă două fire de execuție încearcă să intre în ''​CriticalSection''​ simultan, doar unul dintre ele va reuși, și își va continua execuția în interiorul secțiunii critice, iar celălalt se va bloca pînă când obiectul ''​CriticalSection''​ va fi părăsit de primul fir. Așadar, la sfârșitul secțiunii, primul fir trebuie să părăsească obiectul ''​CriticalSection'',​ permițându-i celuilalt intrarea.+Pentru serializarea accesului la o secțiune critică, fiecare fir de execuție va trebui să intre într-un obiect ''​CriticalSection''​ la începutul secțiunii și să-l părăsească la sfârșitul ei. În acest fel, dacă două fire de execuție încearcă să intre în ''​CriticalSection''​ simultan, doar unul dintre ele va reuși, și își va continua execuția în interiorul secțiunii critice, iar celălalt se va bloca până când obiectul ''​CriticalSection''​ va fi părăsit de primul fir. Așadar, la sfârșitul secțiunii, primul fir trebuie să părăsească obiectul ''​CriticalSection'',​ permițându-i celuilalt intrarea.
  
-Pentru excluderea mutuală se pot folosi atât obiecte [[http://​msdn.microsoft.com/​en-us/​library/​ms684266%28VS.85%29.aspx|Mutex]],​ cât și obiecte [[http://​msdn.microsoft.com/​en-us/​library/​ms682530%28VS.85%29.aspx|CriticalSection]];​ dacă sincronizarea trebuie făcută doar între firele de execuție ale aceluiași proces este recomandată folosirea ''​CriticalSection'',​ fiind un mecanism mai **eficient**. Operația de intrare în ''​CriticalSection''​ se traduce într-o singură instrucțiune de asamblare de tip //​test-and-set-lock//​ (''​TSL''​). ''​CriticalSection''​ este **echivalentul** futex-ului din Linux.+Pentru excluderea mutuală se pot folosi atât obiecte [[http://​msdn.microsoft.com/​en-us/​library/​ms684266%28VS.85%29.aspx|Mutex]],​ cât și obiecte [[http://​msdn.microsoft.com/​en-us/​library/​ms682530%28VS.85%29.aspx|CriticalSection]];​ dacă sincronizarea trebuie făcută doar între firele de execuție ale aceluiași proces este recomandată folosirea ''​CriticalSection'',​ fiind un mecanism mai **eficient**. Operația de intrare în ''​CriticalSection''​ se traduce într-o singură instrucțiune de asamblare de tip //​test-and-set-lock//​ (''​TSL''​). ''​CriticalSection''​ este **echivalentul** ​[[https://​ocw.cs.pub.ro/​courses/​so/​laboratoare/​laborator-08#​futex-uri|futex-ului]] din Linux.
  
 === Inițializarea/​distrugerea unei secțiuni critice === === Inițializarea/​distrugerea unei secțiuni critice ===
Line 526: Line 601:
  
 <note important>​ <note important>​
-Obiectele eveniment de pe Windows sunt diferite de variabilele de conditie ​de pe Linux.\\  +Obiectele eveniment de pe Windows sunt diferite de variabilele de condiție ​de pe Linux.\\  
-Daca se face signal pe un eveniment, ​si nu exista ​un thread care asteapta ​la acel eveniment, acest semnal nu va fi retinut.\\  +Dacă se face signal pe un eveniment, ​și nu există ​un thread care așteaptă ​la acel eveniment, acest semnal nu va fi reținut.\\  
-In momentul ​in care vine un thread ​si asteapta ​la un eveniment ​DUPA ce s-a dat un semnal, acesta ​ramane ​blocat ​pana cand alt thread mai trimite ​inca un semnal.+În momentul ​în care vine un thread ​și așteaptă ​la un eveniment ​**după** ​ce s-a dat un semnal, acesta ​rămâne ​blocat ​până când alt thread mai trimite ​încă ​un semnal.
 </​note>​ </​note>​
 ==== Operații atomice cu variabile partajate (Interlocked Variable Access) ==== ==== Operații atomice cu variabile partajate (Interlocked Variable Access) ====
Line 614: 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 638: 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 653: Line 727:
 ); );
 </​code>​ </​code>​
 +</​spoiler>​
 ==== Timer Queues ==== ==== Timer Queues ====
  
Line 729: 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 760: 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 772: 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 796: 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 805: 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 818: 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
 (revedeți secțiunea despre [[#​timer_queues | Timer Queues]]). (revedeți secțiunea despre [[#​timer_queues | Timer Queues]]).
  
-==== Exercițiul 6 - Barrier ​(2p) ====+==== Exercițiul 6 - Barrier ====
  
 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 837: 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 845: Line 940:
 Folosiți mutexul pentru a sincroniza execuția în cadrul funcției ''​WaitThresholdBarrier''​. Folosiți eventul pentru a aștepta până când toate threadurile ajung să apeleze funcția ''​WaitThresholdBarrier''​. Folosiți funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms684914%28VS.85%29.aspx | PulseEvent]] pentru a semnala toate threadurile care așteaptă asupra eventului. Folosiți mutexul pentru a sincroniza execuția în cadrul funcției ''​WaitThresholdBarrier''​. Folosiți eventul pentru a aștepta până când toate threadurile ajung să apeleze funcția ''​WaitThresholdBarrier''​. Folosiți funcția [[http://​msdn.microsoft.com/​en-us/​library/​ms684914%28VS.85%29.aspx | PulseEvent]] pentru a semnala toate threadurile care așteaptă asupra eventului.
 ==== BONUS ==== ==== BONUS ====
-=== 2 so karma - Parallel Sort ===+=== Parallel Sort ===
  
 Deschideți sursa ''​sort.c''​ din proiectul ''​7-sort''​. Se dorește realizarea sortării unui șir de numere aleatoare dintr-un fișier în următorul mod: Deschideți sursa ''​sort.c''​ din proiectul ''​7-sort''​. Se dorește realizarea sortării unui șir de numere aleatoare dintr-un fișier în următorul mod:
       * Se împarte vectorul în bucăți către fiecare fir de execuție       * Se împarte vectorul în bucăți către fiecare fir de execuție
       * Un fir de execuție sortează bucata proprie folosind quicksort       * Un fir de execuție sortează bucata proprie folosind quicksort
-      * Se face merge la bucăți, în următorul fel: {{so:​laboratoare-2013:​parallel_sort.png?​450}}+      * Se face merge la bucăți, în următorul fel:  {{so:​laboratoare-2013:​parallel_sort.png?​550}}
  
 Realizați partea de creare a firelor de execuție și împărțire a taskurilor în funcția ''​init_setup()''​. După ce toate firele de execuție sortează chunk-ul static, unele vor incepe sa facă merge la chunk-urile sortate. Completați funcția ''​ThreadFunc''​ pentru ca, în funcție de id, un fir de execuție să apeleze funcția ''​MergeChunks''​ (care realizează interclasarea a doi vectori sortați) ​ (urmăriți comentariile cu //TODO//). Realizați partea de creare a firelor de execuție și împărțire a taskurilor în funcția ''​init_setup()''​. După ce toate firele de execuție sortează chunk-ul static, unele vor incepe sa facă merge la chunk-urile sortate. Completați funcția ''​ThreadFunc''​ pentru ca, în funcție de id, un fir de execuție să apeleze funcția ''​MergeChunks''​ (care realizează interclasarea a doi vectori sortați) ​ (urmăriți comentariile cu //TODO//).
Line 857: Line 952:
 Șirul de numere este dat sub forma unui fișier binar care poate fi generat cu programul ''​generator.exe''​. Citirea șirului într-un vector este deja realizată în funcția ''​init_setup'',​ iar fiecare fir de execuție primește o structură ''​CHUNK''​ care reprezintă dimensiunea unui vector de sortat, cât și adresa inițială a vectorului. ​ Interclasarea a două structuri CHUNK în care vectorii sunt deja sortați se realizează cu funcția ''​MergeChunks''​. Șirul de numere este dat sub forma unui fișier binar care poate fi generat cu programul ''​generator.exe''​. Citirea șirului într-un vector este deja realizată în funcția ''​init_setup'',​ iar fiecare fir de execuție primește o structură ''​CHUNK''​ care reprezintă dimensiunea unui vector de sortat, cât și adresa inițială a vectorului. ​ Interclasarea a două structuri CHUNK în care vectorii sunt deja sortați se realizează cu funcția ''​MergeChunks''​.
  
-=== 2 so karma - The dorm room problem ===+=== The dorm room problem ===
  
 Deschideți sursa ''​dorm_room.c''​ din proiectul ''​8-dean''​. Se dorește simularea/​modelarea următoarei probleme: decanul și studenții. Se dau următoarele constrângeri:​ Deschideți sursa ''​dorm_room.c''​ din proiectul ''​8-dean''​. Se dorește simularea/​modelarea următoarei probleme: decanul și studenții. Se dau următoarele constrângeri:​
Line 879: 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.1493808209.txt.gz · Last modified: 2017/05/03 13:43 by theodor.stoican
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