560 likes | 762 Views
Sistemsko programiranje. V. poglavlje Upravljanje nitima. Ciljevi. Nakon svladavanja ovog poglavlja trebali bi znati : Opisati upravljanje Windows nitima Koristiti niti u Windows aplikacijama Koristiti niti pomoću funkcija iz C biblioteke Praviti i izvršavati aplikacije s nitima
E N D
Sistemsko programiranje V. poglavlje Upravljanje nitima
Ciljevi Nakon svladavanja ovog poglavlja trebali bi znati: • Opisati upravljanje Windows nitima • Koristiti niti u Windows aplikacijama • Koristiti niti pomoću funkcija iz C biblioteke • Praviti i izvršavati aplikacije s nitima • Opisati raspodjelu poslova i sustave s više procesora
Niti: Dobre i loše strane • Dobre strane • Jednostavniji programski modeli • Brži izvršavanje koda – u većini slučajeva • Iskorištavanje više procesora • Iskorištavanje ugrađenog paralelizma u samoj aplikaciji • Kod je pouzdan, razumljiv i lak za održavanje • Loše strane • Lošija izvedba nego sekvencijalna – u nekim slučajevima • Mogućnost defekta (izvođenja koda na način koji ne želimo)
Dnevni red • 1. Pregled procesa i niti • 2. Upravljanje nitima • 3. Čekanje da nit završi • 4. C biblioteka i niti • 5. Demonstracija: Izgradnja aplikacije s nitima • 6. Prioritet niti, raspodjela, višeprocesorski sustavi
1. dio Pregled procesa i niti
Pregled procesa i niti (1/2) • Niti unutar procesa dijele podatke i kod • Svaka nit ima svoj vlastiti stog za pozive funkcija • Pozivajuća nit može prosljediti parametar novoj niti u trentuku kreiranja • Taj parametar je na stogu • Svaka nit može zauzeti svoj vlasiti memorijski spremnik (Thread Local Storage - TLS) i postavljati TLS vrijednosti
Pregled procesa i niti (2/2) • Zakazani trenutak izvođenja niti (scheduling) je neovisan, te samo izvođenje niti je neovisno • Izvršioc zakazuje, tj. raspodjeljuje kako će se i kada niti izvoditi • Niti se izvode asinkrono • Može se zaustaviti (pauzirati) izvođenje niti • Ili ponovno restartati u bilo kojem trenutku
Proces i njegove niti Process Kod Globalne varijable Procesni heap Resursi procesa Otvorene datoetke, Heapovi, · · · Blok okruženja (environment) Nit 1 Nit N TLS TLS · · · Stog Stog
Niti koje izvode paralelne zadatke Program s jednom niti Višenitni program Nit 1 Nit 2 Pročitaj datoeku A Pročitaj datoteku A Pročitaj datoteku B Pročitaj datoteku B Čekaj da nit 1 i nit 2 završe Nit 3 Spoji podatke iz obje datoteke Spoji podatke iz obje datoteke Nit 3 Čitanje datoteke B prije datoteke A bi dalo jednak rezultat
2. dio Upravljanje nitima
Upravljanje nitima • Kreiranje niti • Funkcija koja je ulazna točka u nit • Završavanje niti • Izlazni kodovi niti • Identitet niti • Privremeno zaustavljanje i nastavljanje izvršavanja niti
Kreiranje niti (1/6) • Odrediti početnu adresu niti unutar koda procesa • Odrediti veličinu stoga, a stog će zauzeti mjesto unutar adresnog prostora procesa • Stog se ne može naknadno proširiti
Kreiranje niti (2/6) • Odrediti pokazivač na parametar za nit • Može biti pokazivač na bilo koji tip • Interpretira se od strane same niti • CreateThread vraća kao rezultat i ID od niti i njen handle • Vrijednost NULL od handle-a govori da je došlo do greške
Kreiranje niti (3/6) HANDLE CreateThread ( LPSECURITY_ATTRIBUTES lpsa, DWORD cbStack, LPTHREAD_START_ROUTINE lpStartAddr, LPVOID lpvThreadParm, DWORD dwCreate, LPDWORD lpIDThread )
Kreiranje niti (4/6) • Parametri lpsa • Struktura sa sigurnosnim atributima (koristiti NULL) cbStack • Veličina u byte-ovima za stog novokreirane niti • Staviti 0 za preddefiniranu vrijednost primarne niti (od 1MB)
Kreiranje niti (5/6) lpStartAddr • Pokazivač na adresu funkcije (unutar pozivajućeg procesa) koja se izvršava • Ta funkcija prihvaća jedan pokazivač kao parametar i vraća 32-bitni DWORD izlazni kod niti • Nit može interpretirati argument kao DWORD ili kao pokazivač lpThreadParm • pokazivač koji se prenosi kao parametar u nit
Kreiranje niti (6/6) dwCreate • Ako je 0, nit se automatski izvršava • Ako je CREATE_SUSPENDED, nova nit će biti u stanju suspenzije, te će se moći nastaviti izvršavati tek nakon poziva ResumeThread funkcije lpIDThread • Pokazivač na DWORD koji prima ID koji je pridjeljen to j niti; NULL smijemo staviti na W2000/NT
Funkcija koja je ulazna točka u nit Ovako mora izgledati funkcija koja se izvodi prilikom startanja određene niti: DWORD WINAPI MyThreadFunc ( PVOID pThParam ) { . . . ExitThread (ExitCode); /* OR */ return ExitCode; }
Završavanje niti (1/3) • Sve niti se završavaju s pozivom ExitProcess • Proces i sve njegove niti završavaju s izvođenjem • Izlazni kod koji je vraćen od primarne niti procesa je isti kao izlazni kod cijelog procesa • Ili nit može jednostavno završiti i vratiti svoj izlazni kod
Završavanje niti (2/3) • ExitThread je preferirana tehnika • Prilikom izlaska oslobađa se prostor zauzet za stog VOID ExitThread (DWORD dwExitCode) • Kada završi zadnja nit u procesu, tada završava i sam proces.
Završavanje niti (3/3) • Može se završiti drugu nit iz tekuće niti pozivom TerminateThread • Opasno: Neće se osloboditi memorijski prostor stoga niti i drugih resursa • Bolje je ostaviti da nit sama završi (ako se može) • Nit će ostati u sistemu sve dok se zadnji handle unutar nje ne zatvori (koristeći CloseHandle) • Nakon toga nit će biti obrisana • Sve ostale niti mogu dobiti izlazni kod niti koja je završila
Izlazni kodovi niti BOOL GetExitCodeThread ( HANDLE hThread, LPDWORD lpdwExitCode ) lpdwExitCode • Sadrži izlazni kod niti • Može poprimiti vrijednost STILL_ACTIVE
Identitet niti (1/2) • Nit ima stalni “ThreadId” (za cijelo vrijeme svog “života”) • Niti se obično pristupa preko njenog HANDLE-a • ID se može pretvoriti u HANDLE
Identitet niti (2/2) HANDLE GetCurrentThread (VOID); DWORD GetCurrentThreadId (VOID); HANDLE OpenThread ( DWORD dwDesiredAccess, BOOL InheritableHandle, DWORD ThreadId ); /* >= verzije Windows 2000*/
Privremeno zaustavljanje i nastavljanje izvođenja niti (1/2) • Svaka nit ima brojač koji određuje njenu suspenziju • Nit se može izvoditi samo ako je taj brojač jednak 0 • Nit se može kreirati odmahu stanju suspenzije • Nit može uvećavati ili umanjivati brojač suspenzije od druge niti: DWORD ResumeThread (HANDLE hThread)
Privremeno zaustavljanje i nastavljanje izvođenja niti (2/2) DWORD SuspendThread (HANDLE hThread) • Obje funkcije vraćaju prethodno stanje brojača suspenzije • 0xFFFFFFFF ukazuje na pogrešku • Korisno ako želimo postići istovremeni start svih niti • Ne dopuštamo da se niti startaju dok ne kompletiramo inicijalizaciju • Upotreba nesigurna za opću sinkroniczaciju
3. dio Čekanje da nit završi
Čekanje da nit završi (1/2) Čekanje na niti se obavlja tako da se koriste funkcije za čekanje opće namjene WaitForSingleObjectili WaitForMultipleObjects • Koriste se handle-ovi od niti Funkcije za čekanje čekaju da se to signalizira preko handle-a od niti • handle od niti je signaliziran kada nit završi s radom
Čekanje da nit završi (2/2) • ExitThread i TerminateThread postavljaju objekte u signalizirano stanje • Oslobađajući sve druge niti koje čekaju na taj objekt • ExitProcess stavlja proces i sve njegove niti u signalizirano stanje
Funkcije za čekanje (1/2) DWORD WaitForSingleObject ( HANDLE hObject, DWORD dwTimeOut )
Funkcije za čekanje (2/2) DWORD WaitForMultipleObjects ( DWORD cObjects, LPHANDLE lphObjects, BOOL fWaitAll, DWORD dwTimeOut ) Vraća: Razlog završetka čekanja
Opcije čekanja (1/2) Određuje se ili jedan handle hObject Ili cijeli niz cObjects s početnom adresom lphObjects cObjects ne smije premašiti vrijednost MAXIMUM_WAIT_OBJECTS - 64
Opcije čekanja (2/2) • dwTimeOut je u milisekundama • 0 znači da funkcija trenutno završava i samo testira stanje navedenih objekata (ili samo jednog objekta) • Koristi se INFINITE ako nema vremena čekanja • Čekaj zauvijek da neka nit završi • GetExitCodeThread • Vraća izlazni kod niti
Vrijednosti koje vraća funkcija čekanja (1/3) fWaitAll • Ako je TRUE, čeka se na završetak svih niti Moguće vrijednosti koje se vraćaju: • WAIT_OBJECT_0 • Nit je završena (ako pozivamoWaitForMultipleObjects;fWaitAll postavljena)
Vrijednosti koje vraća funkcija čekanja (2/3) • WAIT_OBJECT_0 + n gdje je 0 <= n < cObjects • Ako oduzmemo WAIT_OBJECT_0 od vraćene vrijednosti, onda možemo točno odrediti koja je nit završila pri pozivuWaitForMultipleObjects s postavljenim fWaitAll na FALSE • WAIT_TIMEOUT • Vrijeme čekanja je isteklo
Vrijednosti koje vraća funkcija čekanja (3/3) • WAIT_ABANDONED • Nije moguće čekanje sa zadanim handle-ovima niti • WAIT_FAILED • Treba pozvatiGetLastError za specifični kod greške za niti
4. dio C biblioteka i niti
C biblioteka i niti Skoro svi programi (i funkcije za niti) koriste C biblioteku Ali obična C biblioteka nema neke sigurnosne stavke, tj. nije “thread safe” C funkcija _beginthreadex ima potpuno jednake parametre kao CreateThread
Korištenje _beginthreadex (1/3) Potrebno je vršiti kastanje (cast) izlazne vrijednosti od _beginthreadex na tip(HANDLE) Koristiti _endthreadex namjesto ExitThread #include <process.h>
Korištenje _beginthreadex (2/3) Treba postaviti višenitno okruženje kako slijedi: • #define _MT u svakoj datoteci izvornog koda prije uključivanja <windows.h> • Povezivanje treba vršiti s bibliotekom LIBCMT.LIB • Preddefiniranu biblioteku LIBC.LIB treba zamjeniti s ovom
Korištenje _beginthreadex (3/3) Preferirana metoda ako koristimo Visual C++ Iz menija: • Build Settings — C/C++ Tab • Code Generation category • Odabrati multithreaded run-time library
Primjer: Jednostavna nit “gazda” HANDLE hWork[K]; volatile LONGLONG WorkDone[K], iTh; /* K handle-ova za K šljakera svi šljakeri rade isti posao */ . . . for (iTh = 0; iTh < K; iTh++) { WorkDone[ith] = 0; hWork[iTh] = _beginthreadex (NULL, 0, WorkTh, (PVOID)&iTh, 0, NULL); } WaitForMultipleObjects (K, hWork, TRUE, INFINITE); for (iTh = 0; iTh < K; iTh++) printf (“Thread %d did %d workunits\n”, iTh, WorkDone[iTh]);
Primjer: Jednostavna nit “šljaker” DWORD WINAPI WorkTh (PVOID pThNum) { DWORD ThNum = (DWORD)(*pThNum); while (. . .) { /* šljakaj */ WorkDone[ThNum]++; } _endthreadex (0); }
5. dio Demonstracija: Izgradnja aplikacije s nitima - pomoću npr. VC 6.0
Demonstracija (1/3) Implementaciaj višenitnog programa za brojanje riječi wcMT • Uključiti izvorni kod programa za brojanje riječi wc i izvršavati ga kao nit • Kreirati po jednu nit za svaku datoteku koju treba analizirati • Usporediti izvedbu kada se izvršava kao jedan proces, kao višeprocesna i kao višenitna implementacija • Ako se koriste C biblioteka, napraviti odna “thread-safe” verziju! (znači koristiti CreateThread i pripadajuće funkcije)
Demonstracija (2/2) OVO JE “GAZDA”/”ŠLJAKER” PRINCIP • To je uobičajena tehnika upravljanja nitima Program Resursi Šljakeri Nit 0 Gazda Datoteke main () Nit 1 Nit K-1
6. dio Prioritet niti, raspodjela, višeprocesorski sustavi
Prioritet niti i zakazivanje izvršenja Windows jezgra izvodi nit s najvišim prioritetom koja je spremna za izvršenje 4 klase prioriteta se postavljaju s CreateProcess • IDLE_PRIORITY_CLASS (osnovni prioritet 4) • NORMAL_PRIORITY_CLASS (9 ili 7) • HIGH_PRIORITY_CLASS (13) • REALTIME_PRIORITY_CLASS (24) Windows XP proširenja
Prioritet niti (1/2) Mjenjanje ili određivanje prioriteta niti • Za sebe (tekuću nit) • Za drugi proces, ako smijemo, tj. određuju atributi sigurnosti DWORD SetPriorityClass ( HANDLE hProcess, DWORD dwPriority) DWORD GetPriorityClass ( HANDLE hProcess)
Prioritet niti (2/2) Prioriteti niti se postavljaju relativno na osnovni prioritet • THREAD_PRIORITY_LOWEST • THREAD_PRIORITY_BELOW_NORMAL • THREAD_PRIORITY_NORMAL • THREAD_PRIORITY_ABOVE_NORMAL • THREAD_PRIORITY_HIGHEST