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