This shows you the differences between two versions of the page.
|
so:laboratoare:laborator-09 [2021/05/02 17:29] teodor_stefan.dutu [Semafor Win32] |
so:laboratoare:laborator-09 [2022/05/06 13:38] (current) costin.carabas [Exercițiul 6 - Barrier] |
||
|---|---|---|---|
| Line 427: | Line 427: | ||
| 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~~ | ||