Synchronizace 13.06.18 vjj 1
13.06.18 vjj 2 Synchronizace obraz a zvuk
13.06.18 vjj 3 Synchronizace procesy/vlákna
13.06.18 vjj 4 čeho?!?!?! Synchronizace dvou (a více) paralelně běžících procesů dvou (a více) paralelně běžících vláken jednoho procesu dvou (a více) paralelně běžících vláken dvou (a více) procesů
13.06.18 vjj 5 proč?!?!?! Synchronizace sdílení dat čekání na zprávu, že data jsou připravena exkluzivní práce se sdílenými daty (kritické sekce)
13.06.18 vjj 6 1. proces / vlákno synchronizace I. Signalizace (že data jsou už připravena) 2. proces / vlákno Čekání (až budou data připravena)
13.06.18 vjj 7 příklad F1 () { }... return X ; F2 () { }... return Y ; main (... ) { A = F1() ; B = F2() //nezávislé na výsledku F1 } ; F3(A,B)
13.06.18 vjj 8 příklad F1 () { }... return X ; F2 () { }... return Y ; main (... ) { B = F2() ; A = F1() //nezávislé na výsledku F2 } ; F3(A,B)
13.06.18 vjj 9 problém F1 () { }... return X ; F2 () { }... return Y ; main (... ) { A = F1() ; B = F2() ; čekej na F1 a F2 } ; F3(A,B)
13.06.18 vjj 10 problém F1 () { }... return X ; F2 () { }... return Y ; main (... ) { CreateThread (..., F1,... ) ; CreateThread (..., F2,... ) ; čekej na F1 a F2 } ; F3(A,B)
13.06.18 vjj 11 to umí každý F1 () {... HOTOVO1 = TRUE ; return X ; } F2 () {... HOTOVO2 = TRUE ; return Y ; } main (... ) { bool HOTOVO1 = FALSE ; bool HOTOVO2 = FALSE ; CreateThread (..., F1,... ) ; CreateThread (..., F2,... ) ; while (! ( HOTOVO1 and HOTOVO2 ) ) ; } ; F3(A,B)
13.06.18 vjj 12 malé vylepšení F1 () {... HOTOVO1 = TRUE ; return X ; } F2 () {... HOTOVO2 = TRUE ; return Y ; } main (... ) { bool HOTOVO1 = FALSE ; bool HOTOVO2 = FALSE ; CreateThread (..., F1,... ) ; CreateThread (..., F2,... ) ; while (! ( HOTOVO1 and HOTOVO2 ) ) Sleep(...) ; } ; F3(A,B)
13.06.18 vjj 13 ale jde to i jinak F1 () {... } SetEvent (HOTOVO1) ; return X ; F2 () {... } SetEvent (HOTOVO2) ; return Y ; main (... ) { event HOTOVO1 = CreateEvent (NULL, true, false, ""); event HOTOVO2 = CreateEvent (NULL, true, false, ""); CreateThread (..., F1,... ) ; CreateThread (..., F2,... ) ; Wait ( HOTOVO1, HOTOVO2 ) ; } ; F3(A,B)
13.06.18 vjj 14 a ještě jinak F1 () { }... return X ; F2 () { }... return Y ; main (... ) { mythread1 = CreateThread (..., F1,... ) ; mythread2 = CreateThread (..., F2,... ) ; Wait (mythread1, mythread2 ) ; } ; F3(A,B)
13.06.18 vjj 15 Event HANDLE CreateEvent (LPSECURITY_ATTRIBUTES lpsa, BOOL bmanualreset, BOOL InitialState, LPCTSTR lpszname) ; HANDLE OpenEvent (DWORD fdwaccess, BOOL finherit, LPCTSTR lpszname) ;
13.06.18 vjj 16 Event BOOL SetEvent (hevent) ; Wait... manuální - zůstane nastaven automatický - zruší se nastavení BOOL ResetEvent (hevent) ; BOOL PulseEvent (hevent) ; Wait... manuální - všichni automatický - první
13.06.18 vjj 17 AutoResetEvent static AutoResetEvent autoev = new AutoResetEvent (false) ; po zasignalizování propustí jedno vlákno a opět přejde do nesignalizovaného stavu autoev. Set () ; autoev. WaitOne () ; autoev. WaitOne (int ms, false) ; autoev. Reset () ;
13.06.18 vjj 18 ManualResetEvent static ManualResetEvent autoev = new ManualResetEvent (false) ; po zasignalizování propouští všechna vlákna dokud není explicitně resetován autoev. Set () ; autoev. WaitOne () ; autoev. WaitOne (int ms, false) ; autoev. Reset () ;
synchronizace II. 13.06.18 vjj 19
13.06.18 vjj 20 kritické sekce 1. proces / vlákno... úprava fotografie v RAM... 2. proces / vlákno... jiná úprava fotografie v RAM...
13.06.18 vjj 21 kritické sekce 1. proces / vlákno Wait... úprava fotografie v RAM Release... 2. proces / vlákno Wait... jiná úprava fotografie v RAM Release...
13.06.18 vjj 22 binární semafor Dijkstra proměnná typu integer hodnota 1 znamená volno/pokračuj hodnota 0 znamená obsazeno/čekej Wait(S) : if S = 0 (zavřeno)... čekej ve frontě if S = 1 (otevřeno)... kritické místo S <- 0 (zavři)... a pokračuj Signal(S) : S <- 1 (otevři) vzbuď (první) vlákno, které čeká ve frontě před semaforem S
Wait co znamená "čekej ve frontě"??? TryToGetThrough: if S = 0..... goto TryToGetThrough kdyby to takhle jednoduše dělali i ostatní, nebyla by to fronta (proč?)
13.06.18 vjj 25 Wait DWORD WaitForSingleObject (HANDLE hobject, DWORD milisekundy) ; 0... 0xFFFFFFFF = INFINITE WAIT_OBJECT_0 WAIT_TIMEOUT WAIT_ABANDONED WAIT_FAILED DWORD WaitForSingleObjectEx (HANDLE hobject, DWORD milisekundy, BOOL falert) ;
13.06.18 vjj 26 Wait DWORD WaitForMultipleObjects (DWORD počet, CONST HANDLE * lphobjects, BOOL fwaitall, DWORD milisekundy) ; MAXIMUM_WAIT_OBJECTS WAIT_OBJECT_0 WAIT_OBJECT_0...... WAIT_OBJECT_0 + počet 1 WAIT_TIMEOUT WAIT_ABANDONED_0 WAIT_ABANDONED_0...... WAIT_ABANDONED_0 + počet 1 WAIT_FAILED
13.06.18 vjj 27 Čekání na "už mám čas" DWORD WaitForInputIdle (HANDLE hprocess, DWORD milisekundy) ; PostMessage (hwin2, WM_KEYDOWN, VK_MENU, 0); PostMessage (hwin2, WM_KEYDOWN, VK_F, 0); PostMessage (hwin2, WM_KEYUP, VK_F, 0); PostMessage (hwin2, WM_KEYUP, VK_MENU, 0); PostMessage (hwin2, WM_KEYDOWN, VK_O, 0); PostMessage (hwin2, WM_KEYUP, VK_O, 0); WaitForInputIdle (hproc2, 120000) ;
13.06.18 vjj 28 Kombinované čekání DWORD MsgWaitForMultipleObjects (DWORD počet, LPHANDLE phandles, BOOL ČekejNaVšechny, DWORD Milisekundy, DWORD ZpůsobČekání); Jeden nebo všechny (v závislosti na třetím parametru) objekty signalizují konec Specifikovaný typ vstupu se nově objevil ve frontě vlákna Uplynul předepsaný interval
13.06.18 vjj 29 QS_ALLINPUT QS_HOTKEY QS_INPUT QS_KEY QS_MOUSE QS_MOUSEBUTTON QS_MOUSEMOVE QS_PAINT QS_POSTMESSAGE QS_SENDMESSAGE QS_TIMER Způsob čekání
13.06.18 vjj 30 Semafor HANDLE hsemafor = CreateSemaphore (LPSECURITY_ATTRIBUTES lpsa, LONG MaxValue, LPSTR lpszname) ; HANDLE hsemafor = OpenSemaphore (DWORD Access, BOOL finherit, LPCTSTR lpszname) ; BOOL ReleaseSemaphore (HANDLE hsemaphore, LONG Počet, LPLONG PůvodníPočet) ;
13.06.18 vjj 31 parametr "access" 1. parametr funkce OpenSemaphor SEMAPHORE_ALL_ACCESS úplný přístup SEMAPHORE_MODIFY_STATE použití v ReleaseSemaphor SYNCHRONIZE použití v libovolné funkci Wait...
13.06.18 vjj 32 1. proces / vlákno sdílení semaforu HANDLE hsemafor ; hsemafor = CreateSemaphore (..., "mys") ; 2. proces / vlákno HANDLE hsemafor ; hsemafor = CreateSemaphore (..., "mys") ; if (GetLastError() == ERROR_ALREADY_EXISTS) { // je to sdílený semafor
13.06.18 vjj 33 Mutex HANDLE CreateMutex (LPSECURITY_ATTRIBUTES lpsa, BOOL InitialOwner, LPSTR lpszmutexname) ; HANDLE OpenMutex (DWORD fdwaccess, BOOL finherit, LPCTSTR lpszmutexname) ; MUTEX_ALL_ACCESS SYNCHRONIZE BOOL ReleaseMutex (HANDLE mutex) ;
13.06.18 vjj 34 Mutex using System. Threading ; static Mutex gm1 ; gm1. WaitOne () ; gm1. WaitOne (Timeout.Infinite, false) ; gm1. ReleaseMutex () ;
13.06.18 vjj 35 Mutex static Mutex gm2 ; static Mutex[] gms = new Mutex[2] ; gms[0] = gm1 ; gms[1] = gm2 ; Mutex. WaitAll (gms) ; Mutex. WaitAny (gms) ; if (gm1.waitone (0, false))
13.06.18 vjj 36 aplikace Synchronizační objekty binární semafor vícehodnotový semafor mutex událost, event monitor system semaphor spin-lock
13.06.18 vjj 37 System semaphor omezený počet spinlock test-and-set fronty čekajících vláken nemůže dojít během práce se semaforem a s frontami čekajících vláken k přerušení a přepnutí kontextu? IRQL = "hardwarové priority" - režim procesoru
IRQL Windows Priority 13.06.18 vjj 38
tak v čem je problém? 13.06.18 vjj 39
13.06.18 vjj 40 tak v čem je problém? DEADLOCK
13.06.18 vjj 41 deadlock producent x konzument
producent x konzument v průměru pracují producent i konzument přibližně stejně rychle, krátkodobě se jejich rychlosti mohou podstatně lišit producent konzument ~~~ compile (X) F ~~~... send (X, F) receive (X, F) ~~~ process (X) ~~~
13.06.18 vjj 43 pravidla / podmínky producent zapisuje do fronty stále dokola, ale nesmí přepsat data, která konzument ještě nepřečetl nebo je právě čte konzument čte z fronty stále dokola, ale nesmí předběhnout producenta a číst data, která už jednou přečetl, nebo data, která producent právě zapisuje
13.06.18 vjj 44 producent x konzument sdílení dat -> semaphore Allow binary (0 / 1) producent konzument ~~~ compile(x) ~~~ Wait(Allow) send (X, F) Signal(Allow) F... Wait(Allow) receive (X, F) Signal(Allow) ~~~ process (X) ~~~
13.06.18 vjj 45 producent x konzument sdílení dat -> semaphore Allow binary (0 / 1) čekání na událost producent konzument ~~~ compile (X) ~~~ if (Empty(F)) { Wait(Allow) send (X, F) Signal(Allow) } F... if (Full(F)) { Wait(Allow) receive (X, F) Signal(Allow) } ~~~ process (X) ~~~
13.06.18 vjj 46 producent x konzument sdílení dat -> semaphore Allow binary (0 / 1) čekání na událost producent konzument ~~~ compile (X) ~~~ Wait(Allow) if (Empty(F)) { send (X, F) } Signal(Allow) F... Wait(Allow) if (Full(F)) { receive (X, F) } Signal(Allow) ~~~ process (X) ~~~
13.06.18 vjj 47 producent x konzument semaphore Allow binary (0 / 1) semaphore Empty 0, 1,..., N (= queue capacity) semaphore Full 0, 1,..., N (= queue capacity) producent konzument ~~~ compile (X) ~~~ Wait(Allow) Wait(Empty) send (X, F) Signal(Full) Signal(Allow) F... Wait(Full) Wait(Allow) receive (X, F) Signal(Allow) Signal(Empty) ~~~ process (X) ~~~
13.06.18 vjj 48 producent x konzument nastane-li delší zdržení Konzumenta, může nastat situace, kdy Empty = 0 a Full = N pokud se Producent pokusí zapsat do fronty, zůstane čekat, dokud konzument něco z fronty nepřečte producent konzument ~~~ compile (X) ~~~ Wait(Allow) Wait(Empty) send (X, F) Signal(Full) Signal(Allow) x x x x F... x x x Wait(Full) Wait(Allow) receive (X, F) Signal(Allow) Signal(Empty) ~~~ process (X) ~~~
13.06.18 vjj 49 producent x konzument Konzument se konečně vzpamatuje a pokusí se něco z fronty přečíst Ale přístup k frontě je zablokován... producent konzument ~~~ compile (X) ~~~ Wait(Allow) Wait(Empty) send (X, F) Signal(Full) Signal(Allow) x x x x F... x x x Wait(Full) Wait(Allow) receive (X, F) Signal(Allow) Signal(Empty) ~~~ process (X) ~~~
13.06.18 vjj 50 nebezpečí: deadlock (deadly embrace) Deadlock podmínky nutné pro vznik deadlocku protiopatření
Dining philosophers 13.06.18 vjj 51
13.06.18 vjj 52 Dining philosophers problem It was originally formulated in 1965 by Edsger Wybe Dijkstra as a student exam exercise, presented in terms of computers competing for access to tape drive peripherals. Soon after, Tony Hoare gave the problem its present formulation.
13.06.18 vjj 53 Dining philosophers problem Plato Confucius Socrates Voltaire Descartes
13.06.18 vjj 54 philosopher's algorithm think until the left fork is available; when it is, pick it up; think until the right fork is available; when it is, pick it up; when both forks are held, eat for a fixed amount of time; then, put the right fork down; then, put the left fork down; repeat from the beginning
deadlock 13.06.18 vjj 55
Dining philosophers problem P1 P2 get LEFT fork LEFT fork RIGHT get LEFT fork get RIGHT fork get RIGHT fork dining release RIGHT fork release LEFT fork RIGHT fork LEFT dining release RIGHT fork release LEFT fork
Dining philosophers problem P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
start P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
za chvíli P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
a dále P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
a dále P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
a dále P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
a dále P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
a dále P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
a dále - deadlock P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
13.06.18 vjj 66 detekce deadlocku dynamický orientovaný graf kdo na co čeká co komu patří vznikne-li cyklus, nastal deadlock
deadlock P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
deadlock P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
13.06.18 vjj 69 podmínky vzniku deadlocku Coffman conditions (pro binární semafory) 1. mutual exclusion vzájemná vyloučenost prostředek má vždy jen jediného (dočasného) uživatele/vlastníka (tj. nikdo jiný ho nesmí používat) 2. přidělování bez preempce (bez odebírání) prostředek může uvolnit jen jeho (dočasný) majitel, tj. cyklické čekání nelze násilně ukončit nějakým externím správcem 3. hold and wait tj. mohu blokovat jeden prostředek a přitom čekat na jiný, resp. o prostředky lze žádat postupně/jednotlivě a v libovolném pořadí 4. může dojít k cyklickému čekání
13.06.18 vjj 70 deadlock aby mohlo dojít k deadlocku, musí "systém" splňovat všechny Coffmanovy podmínky najednou k tomu, aby byl "systém" bezpečný (tj. aby nemohlo dojít k cyklickému čekání) stačí, aby v systému alespoň jedna z těchto podmínek neplatila
13.06.18 vjj 71 virtuální prostředky 1. prevence deadlocku každý si myslí, že má žádaný prostředek sám pro sebe a taky se tak chová (např. tiskárny - Spooling) není nutné omezovat přístup k prostředkům není zapotřebí prostředky jejich majitelům odebírat
13.06.18 vjj 72 preempce 2. ukončení deadlocku jeden z procesů je zavražděn (proč?) prostředky, které vlastnil, jsou opět volné násilné odebírání prostředků slouží jen k rozseknutí už vzniklého deadlocku
13.06.18 vjj 73 3. prevence deadlocku a) povinnost žádat o všechny používané prostředky najednou Wait (F1, F2) dining Signal (F1) Signal (F2)
13.06.18 vjj 74 3. prevence deadlocku b) hierarchie prostředků pokud proces potřebuje více prostředků najednou, musí o ně žádat v předepsaném pořadí Wait (F1) Wait (F2) dining Signal (F2) Signal (F1)
virtuální vidličky (???) P1 fork P2 Wait (F1) Wait (F2) F1 F1 Wait (F2) Wait (F1) dining F2 F2 dining Release (F1) Release (F2) Release (F1) Release (F2) fork
preempce P1 P2 Wait (F1) Wait (F2) dining Release (F1) Release (F2) F1 F2 Wait (F2) Wait (F1) dining Release (F1) Release (F2)
předepsané pořadí P1 P2 Wait (F1) Wait (F2) dining Release (F2) Release (F1) F1 F2 Wait (F1) Wait (F2) dining Release (F2) Release (F1)
vše najednou P1 P2 Wait (F1, F2) F1 Wait (F1, F2) dining Release (F2) Release (F1) F2 dining Release (F2) Release (F1)
13.06.18 vjj 79 4. prevence deadlocku zabránit vzniku cyklu Bankéřův algoritmus Bankéř průběžně financuje investiční projekty svých klientů - bezúročně (!?). Klienti nemusí požadovat celou potřebnou sumu najednou Při žádosti o schválení projektu musí ale každý klient předložit celkový plán financování. Ihned po dokončení projektu klient všechny zapůjčené miliardy vrátí. Nesmí se stát, aby bankéř neměl prostředky na dokončení již započatého projektu. Bankéř může vyplácet jednotlivé části půjčky s libovolně dlouhou prodlevou
13.06.18 vjj 80 1) nový požadavek Bankéřův algoritmus 2) simulace přidělení prostředku 3) zrušení příznaku "skončil" u všech procesů 4) existuje proces, který ještě nemá nastaven příznak "skončil"? a) ano: existuje proces který by za dané situace mohl skončit, tj. má bankéř dost prostředků, aby mohl uspokojit jeho již dříve oznámené požadavky? a. ano: simuluj jeho dokončení, tj. nastav jeho příznak "skončil" a simuluj vrácení všech jeho prostředků, potom se vrať do bodu 4). b. ne: odmítni nový požadavek b) ne: přiděl požadovaný prostředek
13.06.18 vjj 81 nový požadavek Bankéřův algoritmus simulace přidělení prostředku zrušení příznaku "skončil" u všech procesů existuje proces, který ještě nemá nastaven příznak "skončil" a který by za dané situace mohl skončit, tj. má bankéř dost prostředků, aby mohl uspokojit jeho již dříve oznámené požadavky? ano simuluj jeho dokončení, tj. nastav jeho příznak "skončil" a simuluj vrácení všech jeho prostředků ne existuje proces, který ještě nemá nastaven příznak "skončil"? ne ano přiděl požadovaný prostředek odmítni požadavek
13.06.18 vjj 82 příklad bankéř: celkem 12 mld, 10 půjčeno, 2 zbývají přišel požadavek na jednu miliardu s tím, že před jejím vrácením budou požadovány ještě tři další (1+3) zápis: x+y = klient si už vypůjčil x miliard, před jejich vrácením bude (postupně) požadovat ještě y dalších 3+5 1+2 1+1 5+4 2 1+3 3+5 1+2 1+1 5+4 1+3 1 3+5 1+2 1+1 5+4 1+3 2 3+5 1+2 1+1 5+4 1+3 3 3+5 1+2 1+1 5+4 1+3 4 3+5 1+2 1+1 5+4 1+3 9 3+5 1+2 1+1 5+4 1+3 12
13.06.18 vjj 83 bankéř na začátku, tj. když má bankéř ještě všechno, se jedná o tzv. bezpečný stav bezpečný stav - existuje alespoň jedna posloupnost postupného ukončování projektů (procesů), která skončí tím, že bankéř má zase všechno bankéř si vždy ověřuje, vznikne-li splněním nového požadavku opět bezpečný stav, jinak požadavek odmítne v předchozím příkladu se předpokládalo, že počáteční stav je bezpečný (taky že byl), a ověřilo se, že splněním požadavku "1+3" vznikne opět bezpečný stav (tím se dodatečně dokázalo, že i ten předchozí byl bezpečný)
13.06.18 vjj 84 příklad ze stejného bezpečného počátečního stavu by splněním požadavku "1+4" vzniknul nebezpečný stav 3+5 1+2 1+1 5+4 2 3+5 1+2 1+1 5+4 1+4 1 3+5 1+2 1+1 5+4 1+4 2 3+5 1+2 1+1 5+4 1+4 3 dál to nejde, tj. splněním nového požadavku by vzniknul nebezpečný stav, požadavek bude odmítnut 1+4
13.06.18 vjj 85 kritická sekce různá řešení
13.06.18 vjj 86 podmíněná kritická sekce = semafor + podmínka region Přístup when Plných<N do send(x,f) Plných = Plných + 1
13.06.18 vjj 87 Kritická sekce - příprava CRITICAL_SECTION mycritsect ; InitializeCriticalSection (&mycritsect) ; HANDLE hthreads[2] ; hthread[0] = CreateThread (, mythread, ) ; hthread[1] = CreateThread (, mythread, ) ; WaitForMultipleObjects (2, hthreads, TRUE, INFINITE) ; CloseHandle (hthread[0]) ; CloseHandle (hthread[1]) ; DeleteCriticalSection (&mycritsect) ;
13.06.18 vjj 88 kritická sekce - synchronizace 2. a 3. vlákno DWORD WINAPI mythread (LPVOID parm) {... EnterCriticalSection (&mycritsect) ;... kritická sekce } LeaveCriticalSection (&mycritsect) ;... return (0) ;
13.06.18 vjj 89 monitor kritické sekce jsou soustředěny do objektu Monitor, který hlídá jejich exkluzivní volání monitor Přístup function Zapiš(X)... function Přečti(X)... producent Přístup.Zapiš(X) klient Přístup.Přečti(X)
13.06.18 vjj 90.NET Monitor pro práci se sdílenými daty static byte[] buffer = new byte[100] ; static void myfce() { Monitor.Enter( buffer ) ;... Monitor.Exit( buffer ) ; } C# lock( buffer ) {... }
13.06.18 vjj 91.NET Monitor Monitor. Wait (buffer) ; Monitor. Pulse (buffer) ; Monitor. PulseAll (buffer) ;