1 / 47

Sistemsko programiranje

Sistemsko programiranje. VI. poglavlje Sinkronizacija niti. Ciljevi. Nakon svladavanja ovog poglavlja trebali bi znati : Opisati različite mehanizme za sinkronizaciju Razlikovati osobine sinkronizacijskih objekata i kako odabrati odgovarajući Koristiti sinkronizaciju u Windows aplikacijama.

donal
Télécharger la présentation

Sistemsko programiranje

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Sistemsko programiranje VI. poglavlje Sinkronizacija niti

  2. Ciljevi Nakon svladavanja ovog poglavlja trebali bi znati: • Opisati različite mehanizme za sinkronizaciju • Razlikovati osobine sinkronizacijskih objekata i kako odabrati odgovarajući • Koristiti sinkronizaciju u Windows aplikacijama

  3. Dnevni red 1. dio Sinkronizacija i njeni objekti 2. dio Kritični odsječci (CRITICAL_SECTIONs) 3. dio Muteksi za međusobno isključivanje 4. dio Događaji (Events) 5. dio Semafori 6. dio Sažetak

  4. 1. dio Sinkronizacija i njeni objekti

  5. Potreba za sinkronizacijom Zašto je potrebna sinkronizacija niti? Primjeri: • Nit “gazda” ne može se nastaviti dok niti “šljakeri” ne dovrše posao • Nit “šljaker” ne može nastaviti dok se ne inicijalizira njezino okruženje • Nit se ne može nastaviti dok se ne postignu određeni uvjeti, primjerice: • Dok se ne omogući slobodni buffer • Dok se buffer ne popuni podacima da bi se mogao procesirati • Niti ne bi smjele mjenjati vrijednosti istim varijablama istovremeno • Pročitaj-promjeni-zapiši • Bez ispravne sinkronizacije, riskiramo sljedeće defekte: • “uvjeti utrke” (Race conditions) - mogućnost da niti krenu istovremeno • Istovremena promjena jednog resursa

  6. Problem sinkronizacije Nit 1 Nit 2 M N Izvodi se Spremna 4 4 5 M = N; M = M + 1; Izvodi se Spremna M = N; M = M + 1; N = M; 4 5 5 Izvodi se Spremna N = M; 5 · · · · · ·

  7. Objekti sinkronizacije niti • Poznati Windows mehanizmi za sinkronizaciju niti: • Nit može čekati na drugu nit da završi (koristeći ExitThread) čekajući na handle niti koristeći WaitForSingleObject ili WaitForMultipleObjects • Proces može čekati na drugi proces da završi (ExitProcess) na potpuuno istovjetan način • Ostale uobičajene metode (nisu pokrivene u ovom poglavlju): • Čitanje s pipe ili soketa koji dopušta jednom procesu ili niti da čeka na drugi da bi pisao/slao na pipu ili soket • Zaključavanje datoteka je specifično za sinkronizaciju pristupa datotekama

  8. Objekti sinkronizacije • Windows nam omogućava još četiri objekta specijalno dizajnirana za sinkronizaciju niti i procesa • Tri su objekta jezgre (imaju svoje HANDLE-ove) • Događaji (Events) • Semafori • Muteksi • S njima dolaze i složeniji problemi – treba paziti na: • potpuni zastoj • “race conditions” • propušteni signali • mnogi drugi...

  9. Objekti kritičnog odsječka • Kritični odsječci (critical sections) • Četvrti tip objekta koji može sinkronizirati samo niti unutar procesa • Često najefikasniji izbor • Primjenjiv na mnoge scenarije primjene • “Brzi muteksi” • Nije objekt jezgre (kernela) • Objekt kritičnog odsječka se inicijalizira a ne kreira • Briše se, a ne zatvara • Niti ulaze i izlaze iz kritičnih odsječaka • Samo jedna nit istovremeno može biti u određenom kritičnom odsječku • Ne postoji handle — postoji tip CRITICAL_SECTION

  10. 2. dio Kritični odsječci

  11. Kritični odsječci VOID InitializeCriticalSection ( LPCRITICAL_SECTION lpcsCriticalSection) VOID DeleteCriticalSection ( LPCRITICAL_SECTION lpcsCriticalSection) VOID EnterCriticalSection ( LPCRITICAL_SECTION lpcsCriticalSection)

  12. Upravljanje kritičnim odsječcima VOID LeaveCriticalSection ( LPCRITICAL_SECTION lpcsCriticalSection) BOOL TryCriticalSection ( LPCRITICAL_SECTION lpcsCriticalSection)

  13. Korištenje kritičnih odsječaka • EnterCriticalSection blokira nit ako je druga nit već u kritičnom odsječku • Koristeći TryCriticalSection izbjegava se blokiranje • Nit može ući u kritični odsječak više puta (“rekurzivno”) • Nit koja čeka u blokiranom stanju, nastavlja s radom tek kada nit koja je bila unutar kritičnog odsječka izvršila LeaveCriticalSection • Nit mora napustiti kritični odsječak svaki puta kada je ušla u njega • Uobičajena upotreba: dopuštanje niti da pristupi globalnim varijablama • Uvijek treba ove varijable deklarirati kao volatile!

  14. Sinkronizacija kritičnih odsječaka Nit 1 Nit 2 M Izvodi se N Ne radi ništa 4 ECS(&CS); M = N; M = M + 1; 4 5 Ne radi ništa Izvodi se ECS(&CS); Izvodi se Blokirana N = M; 5 LCS (&CS); Izvodi se · · · M = N; M = M + 1; N = M; 5 6 6 LCS(&CS); · · ·

  15. Kritični odsječci i __finally • Metoda koja osigurava da smo napustili kritični odsječak • Čak i ako netko kasnije modificira kod • Ova tehnika također radi kod zaključavanja datoteka i kod drugih sinkronizacijskih objekata koji se dalje diskutiraju

  16. __finally (2/2) CRITICAL_SECTION cs; ... InitializeCriticalSection (&cs); ... EnterCriticalSection (&cs); _try { ... } _finally { LeaveCriticalSection (&cs); }

  17. Komentari o kritičnim odsječcima • Kritični odsječci se testiraju u korisničkom prostoru • Brzo je – bez korištenja jezgre (kernela) • Ali čeka se u prostoru jezgre • Obično brže nego Muteksi (o njima malo poslije) • Ali, ne uvijek • Faktori uključuju broj niti, broj procesora, količinu istog sadržaja niti • Izvedba se ponekad može “podešavati” • Podešavanje “brojača spina” (spin count) – više o tome kasnije • Kritični odsječci rade tako da koriste prozivanje (cikličko ispitivanje), i ekvivalent međusobno zaključanih funkcija

  18. Primjer totalnog zastoja (1/2) • Programski defekt koji može uzrokovati totalni zastoj • Neki funkcijski pozivi su skraćeni zbog jezgrovitosti, tj. kratkoće • Uvijek ulazimo u kritične odsječke redom; izlazimo iz njih u obrnutom redosljedu • Totalni zastoji su specijalna vrsta race condition-a • Kako bi se izbjegli totalni zastoji, treba kreirati hijerarhiju zaključavanja

  19. Primjer totalnog zastoja (2/2) CRITICAL_SECTION csM, csN; volatile DWORD M = 0, N = 0; InitializeCriticalSection (&csM); ICS (&csN); ... DWORD ThreadFunc (...) { ECS (&csM); ECS (&csN); M = ++N; N = M - 2; LCS (&csN); LCS (&csM); ... ECS (&csM); ECS (&csN); M = N--; N = M + 2; LCS (&csN); LCS (&csM); }

  20. Funkcije koje se međusobno zaključavaju (1/3) • Za jednostavnu manipulaciju s predznačnim 32-bitnim brojevima, može se koristiti funkcije koje se međusobno zaključavaju (interlocked) • Sve operacije su najjednostavnije • Operacije se izvode u prostoru korisnika • bez korištenja jezgre (kernela) • Jednostavno, brzo, nema rizika totalnog zastoja • Ograničeno na inkrement, dekrement, zamjenu, zamjenu/dodavanje i usporedbu/zamjenu • Ne mogu direktno riješiti opći problem međusobnog isključenja

  21. Funkcije koje se međusobno zaključavaju (2/3) LONG InterlockedIncrement (LPLONG lpAddend) LONG InterlockedDecrement (LPLONG lpAddend) • Vraća vrijednost rezultata • Koji se može promjeniti prije nego možemo koristiti njegovu vrijednost • LONG InterlockedExchangeAdd (LPLONG lpAddend, LONG Increment) • Vraćena vrijednost je stara vrijednost *lpAddend • LONG InterlockedExchange (LPLONG lpTarget, LONG Value) • Vraćena vrijednost je stara vrijednost *lpTarget

  22. Funkcije koje se međusobno zaključavaju (3/3) LONG InterlockedCompareExchange (LPDORD lpDest, DWORD Exch, DWORD Comparand) • Operandi moraju biti 4-bajtno poravnati • Vraća inicijalnu vrijednost *lpDest • *lpdest = (*lpDest == Comparand) ? Exch : *lpDest

  23. 3. dio Muteksi za međusobno isključivanje

  24. Muteks (Mutex) (1/2) • Muteksi se mogu imenovati i imaju svoj HANDLE • Oni su objekti jezgre (kernela) • Mogu se koristiti za međuprocesnu sinkronizaciju • Njihov vlasnik je ipak nit, radije nego proces • Niti dobivaju vlasništvo nad muteksom čekajući na handle muteksa • koristeći WaitForSingleObject ili WaitForMultipleObjects • Niti oslobađaju vlasništvo pozivomReleaseMutex

  25. Muteks (Mutex) (2/2) • Rekurzivno: nit može dobiti određeni muteks nekoliko puta ali mora ga i osloboditi isti broj puta • Može biti korisno, primjerice, kod ugnježdenih transakcija • Muteks se može odabrati tako da izbjegnemo blokiranje • Muteks postaje “napušten” ako nit koja ga posjeduje završi • Događaji (events) i semafori su drugi objekti jezgre • Vrlo sličan životni ciklus i korištenje

  26. Kreiranje muteksa HANDLE CreateMutex (LPSECURITY_ATTRIBUTES lpsa, BOOL fInitialOwner, LPCTSTR lpszMutexName) • Zastavica fInitialOwner, ako je TRUE, daje trenutno pozivajućoj niti vlasništvo nad novim muteksom • ako imenovani muteks već postoji, onda ne vrijedi • lpszMutexName pokazivač na ime putanje • Imena putanje su osjetljiva na malo i veliko slovo • Imena putanje su 0-terminirani • Muteksi su neimenovani ako je ovaj parametar NULL

  27. Oslobađanje muteksa BOOL ReleaseMutex (HANDLE hMutex) • ReleaseMutex oslobađa muteks koji posjeduje nit koja poziva ovu funkciju • Greška je ako nit ne posjeduje dani muteks • Ako je muteks napušten, čekanje na njega će vratitiWAIT_ABANDONED_0 • Ovo je bazna vrijenost ako se čeka na više • OpenMutex otvara već postojeći imenovani muteks • Dopuštajući nitima u različitim procesima da se sinkroniziraju

  28. Imenovanje muteksa (1/2) • Imenovaćemo muteks koji se koristi u više nego jednom procesu • Muteksi, semafori i događaji dijele isti prostor imena • Objekti memorijskog mapiranja također koriste ovaj isti prostor imena • Također i tajmeri za čekanje (o tome kasnije) • Nije potrebno imenovati muteks koji se koristi unutar jednog procesa

  29. Imenovanje muteksa (2/2) • Interakcija procesa s imenovanim muteksima Proces 1 h = CreateMutex ("MIme"); Proces 2 h = OpenMutex ("MIme");

  30. 4. dio Događaji (Events)

  31. Događaji (1/6) • Događaji (events) mogu odjednom osloboditi nekoliko niti koje čekaju kada to neka nit signalizira • Događaj manual-reset može signalizirati nekoliko niti istovremeno i mora biti postavljen od niti • Događaj auto-reset signalizira jedna nit, a događaj se postavlja automatski • Događaj se signalizira pomoću PulseEvent ili SetEvent • Četiri kombinacije s vrlo različitim ponašanjem • Oprez! Postoje brojni problemi

  32. Događaji (2/6) HANDLE CreateEvent ( LPSECURITY_ATTRIBUTES lpsa, BOOL fManualReset, BOOL fInitialState, LPTCSTR lpszEventName) • Događaj Manual-reset: postavitifManualReset na TRUE • Događaj se inicijalno postavlja na signaliziran ako jefInitialState postavljen na TRUE • Otvoriti imenovani događaj pomoću OpenEvent • Vjerojatno iz nekog drugog procesa

  33. Događaji (3/6) • Tri funkcije za kontroliranje događaja su: BOOL SetEvent (HANDLE hEvent) BOOL ResetEvent (HANDLE hEvent) BOOL PulseEvent (HANDLE hEvent)

  34. Događaji (4/6) • Nit signalizira događaj pomoću SetEvent • Ako je događaj auto-reset, jedna nit koja čeka (vjerojatno jedna od više) bit će oslobođena • Događaj se automatski vraća u nesignalizirano stanje • Ako nijedna nit nije u stanju čekanja, događaj ostaje u signaliziranom stanju sve dok neka nit ne počne čekati na taj događaj i takva nit će biti trenutno oslobođena čekanja

  35. Događaji (5/6) • Ako je događaj manual-reset, događaj ostaje signaliziran sve dok neka nit ne pozove ResetEvent za taj događaj • Tijekom tog vremena, sve niti će biti oslobođene čekanja • Moguće je da će druge niti čekati, i biti otpuštene prije reseta • PulseEvent dopušta nam da oslobodimo sve niti koje trenutno čekaju na događaj manual-reset • Nakon toga događaj se automatski resetira

  36. Događaji (6/6) • Ako koristimo WaitForMultipleEvents, čeka se na sve događaje da budu signalizirani • Nit koja čeka bit će oslobođena tek onda kada su svi događaji simultano u signaliziranom stanju • Neki signalizirani događaji mogu se osloboditi prije nego je nit oslobođena

  37. Pregled događaja • Ponašanje ovisi o tome je li manual ili auto reset, Pulse ili Set Event • Sve 4 forme su korisne AutoResetManualReset SetEvent Točno jedna nit je oslobođena. Sve niti koje trenutno čekaju Ako nijedna nit trenutno ne čeka se oslobađaju. Događaj na događaj, sljedeća nit koja će ostaje signaliziran sve dok čekati bit će oslobođena. se ne resetira od neke druge niti. PulseEventTočno jedna nit je oslobođena, Sve niti koje trenutno čekaju ali samo u slučaju da nit trenutno se oslobašđaju, a događaj se čeka na događaj. nakon toga resetira.

  38. 5. dio Semafori

  39. Semafori (1/4) • Semafor kombinira ponašanje muteksa i događaja • Može se emulirati sa po jednim semaforom, muteksom i brojačem • Semafori održavaju brojač • Nema koncepta vlasništva • Objekt semafora je signaliziran kada je brojač veći od nule, a objekt nije signaliziran kada je brojač jednak nula • S oprezom, može se postići međusobno isključivanje koristeći semafor

  40. Semafori (2/4) • Niti i procesi čekaju na normalan način, koristeći neku od funkcija za čekanje • Kada se nit koja čeka oslobodi, brojač semafora se uveća za jedan • Bilo koja nit se može oslobodit • Nije ograničen samo na nit koja je napravila semafor • Uzmimo u obzir model proizvođač/kupac kako bi vidjeli zašto

  41. Semafori (3/4) HANDLE CreateSemaphore ( LPSECURITY_ATTRIBUTES lpsa, LONG cSemInitial, LONG cSemMax, LPCTSTR lpszSemName) • cSemMax je najveća vrijednost za semafor • Mora biti jedan ili veća • 0 <= cSemInitial <= cSemMax je početna vrijednost • Može se samo dekrementirati (umanjiti za jedan) brojač semafora s bilo kojom operacijom za čekanje, ali semafor se može otpustiti i uvećati njegov brojač za bilo koju vrijednost sve do maksimalne vrijednosti

  42. Semafori (4/4) BOOL ReleaseSemaphore ( HANDLE hSemaphore, LONG cReleaseCount, LPLONG lpPreviousCount) • Može se naći vrijednost brojača u trenutku prije otpuštanja semafora, ali pokazivač može biti i NULL ako nam ova vrijenost nije potrebna • Brojač koji se oslobađa mora biti veći od nule, ali ako će brojač semafora premašiti vrijednost maksimuma, poziv će vratiti FALSE i brojač će ostati nepromjenjen • Postoji također i OpenSemaphore funkcija

  43. Defekt totalnog zastoja kod semafora • Ne postoji jednostavno čekanje na više semaforskih objekata • Ali se može jednostavno osloboditi više objekata odjednom • Ovo je potencialni totalni zastoj unutar funkcije niti for (i = 0; i < NumUnits; i++) WaitForSingleObject (hSem, INFINITE); • Rješenje je da petlju gledamo kao kritčni odsječak, koji se čuva pomoću CRITICAL_SECTION ili muteksa • Ili, višestruki semafor za čekanje može se kreirati koristeći događaj, muteks i brojač

  44. Kritični odsječak Imenovan, Sigurnosni sinkronizacijski objekt Dostupan od višestrukih procesa Sinkronizacija Oslobađanje Vlasništvo Efekt otpuštanja Ne Ne Prilikom ulaska Prilikom izlaska Jedna nit istovremeno. Rekurzivno Može ući jedna nit koja čeka

  45. Muteks Imenovan, Sigurnosni sinkronizacijski objekt Dostupan od višestrukih procesa Sinkronizacija Oslobađanje Vlasništvo Efekt otpuštanja Da Da Prilikom čekanja Oslobađanjem ili ako vlasnik završi Jedna nit istovremeno. Rekurzivno Jenda nit koja čeka može dobiti vlasništvo nakon zadnjeg oslobađanja

  46. Semafor Imenovan, Sigurnosni sinkronizacijski objekt Dostupan od višestrukih procesa Sinkronizacija Oslobađanje Vlasništvo Efekt otpuštanja Da Da Prilikom čekanja Bilo koja nit može osloboditi Nije moguće – Više niti odjednom, sve do najviše vrijednosti Više niti mogu nastaviti, ovisno o broju otpuštanja

  47. Događaj Imenovan, Sigurnosni sinkronizacijski objekt Dostupan od višestrukih procesa Sinkronizacija Oslobađanje Vlasništvo Efekt otpuštanja Da Da Prilikom čekanja Set, Pulse Nije moguće – Svaka nit može postaviti Set ili Pulse načinom Jedna ili više niti koje čekaju će nastaviti nakon Set ili Pulse - Oprez

More Related