790 likes | 918 Views
Les 4: Synchronisatie. If debugging is the process of removing bugs, then programming must be the process of putting them in. (Edsger W. Dijkstra). Overzicht. Wat is synchronisatie? Software-oplossingen Hardware-oplossingen Semafoor Monitor Transactioneel geheugen Boodschappen Impasses.
E N D
Les 4: Synchronisatie If debugging is the process of removing bugs, then programming must be the process of putting them in.(Edsger W. Dijkstra)
Overzicht • Wat is synchronisatie? • Software-oplossingen • Hardware-oplossingen • Semafoor • Monitor • Transactioneel geheugen • Boodschappen • Impasses
Doelstelling • Het in goede banen leiden van de interactie tussen processen of draden • Doorgeven van informatie • Vastleggen van volgorde • Wederzijdse uitsluiting • Bij foute synchronisatie • Raceconditie (‘te weinig synchronisatie’) • Impasse (‘te veel synchronisatie’)
Doorgeven van informatie • B.v.: “ls | wc” • Methoden: • Via bestanden, pipes, sockets • Via boodschappen (send/receive) • Via een blok gemeenschappelijk geheugen • Via kernstructuren (b.v. semaforen) • Bij draden is het gebruik van gemeenschappelijk geheugen het meest voor de hand liggend
Volgorde vastleggen Proces 1 Proces 2 wacht [ A A → B [ signaleer [ B [ t t
Wederzijdse uitsluiting De secties A en B zijn wederzijds uitgesloten Proces 1 Proces 2 Proces 1 Proces 2 [ [ A B [ [ of [ [ A → B A B → A B [ [ t t t t
Gebruik van gemeenschappelijke data Draad 1 Draad 2 teller [ A: teller ++ [ load r1, teller add r1, 1 store teller, r1 load r1, teller sub r1, 1 store teller, r1 [ A → B B: teller-- [ raceconditie t t
Raceconditie fout resultaat Proces 1 Proces 2 Juist [ [ [ [ t t Fout Proces 1 Proces 2 [ [ [ [ t t Juist
Raceconditie var ← 0 var ← 0 var ← 0 lees schrijf schrijf schrijf lees lees r ← var r ← var var ← 5 var ← 3 r ← var var ← 3 r = 0 ? 3 ? var = 3 ? 5 ? r = 0
Kritieke Sectie Ingangssectie Ingangssectie load r1,teller add r1, 1 store r1,teller load r1,teller sub r1, 1 store r1,teller Uitgangssectie Uitgangssectie
Protocolvoorwaarden • wederzijdse uitsluiting garanderen slechts 1 proces per keer in een kritieke sectie • vooruitgang garanderen een lege sectie moet kunnen betreden worden • eindige wachttijden garanderen het aantal vóór te laten processen moet eindig zijn Er mogen geen veronderstellingen over snelheid of over het aantal processors gemaakt worden.
Overzicht • Wat is synchronisatie? • Software-oplossingen • Hardware-oplossingen • Semafoor • Monitor • Transactioneel geheugen • Boodschappen • Impasses
Oplossing met twee processen enteringCriticalSection(0) enteringCriticalSection(1) kritieke sectie 2 kritieke sectie 1 leavingCriticalSection(0) leavingCriticalSection(1) public interface MutualExclusion { public abstract void enteringCriticalSection(int turn); public abstract void leavingCriticalSection(int turn); }
Beurtelings protocol public class Algorithm_1 implements MutualExclusion { private volatile int turn; public Algorithm_1() { turn = 0; } public void enteringCriticalSection(int t) { while (turn != t) Thread.yield(); } public void leavingCriticalSection(int t) { turn = 1 - t; } } Geen vooruitgang gegarandeerd
Hoffelijk protocol public class Algorithm_2 implements MutualExclusion { private volatile boolean flag[2]; public Algorithm 2() { flag[0] = false; flag[1] = false; } public void enteringCriticalSection(int t) { flag[t] = true; while(flag[1-t] == true) Thread.yield(); } public void leavingCriticalSection(int t) { flag[t] = false; } } Geen vooruitgang gegarandeerd
Algoritme van Peterson public class Algorithm_3 implements MutualExclusion { private volatile boolean flag[2]; private volatile int turn; public Algorithm 3() { flag[0] = false; flag[1] = false; turn = 1; } public void enteringCriticalSection(int t) { flag[t] = true; turn = 1-t; while (flag[1-t] == true && turn == 1-t) Thread.yield(); } public void leavingCriticalSection(int t) { flag[t] = false; } } Peterson, algoritme van
Overzicht • Wat is synchronisatie? • Software-oplossingen • Hardware-oplossingen • Semafoor • Monitor • Transactioneel geheugen • Boodschappen • Impasses
Hardware-oplossingen • Afzetten van de onderbrekingen • Read-modify-write instructies • SWAP • TAS • CAS
Afzetten onderbrekingen cli cli kritieke sectie 2 kritieke sectie 1 sti sti
Synchronisatieprotocol met SWAP en actieve synchronisatie locked LOADI R1,1 L: SWAP R1,locked COMPARE R1,1 JE L LOADI R1,1 L: SWAP R1,locked COMPARE R1,1 JE L kritieke sectie 2 kritieke sectie 1 LOADI R1,0 SWAP R1,locked LOADI R1,0 SWAP R1,locked Spin Lock swap r1, [n] ; x := mem[n]; mem[n] := r1; r1 := x;
Synchronisatieprotocol met SWAP en zonder actieve synchronisatie locked LOADI R1,1 L: SWAP R1,locked COMPARE R1,1 JNE OK “CALL YIELD” JMP L OK: LOADI R1,1 L: SWAP R1,locked COMPARE R1,1 JNE OK “CALL YIELD” JMP L OK: kritieke sectie 2 kritieke sectie 1 LOADI R1,0 SWAP R1,locked LOADI R1,0 SWAP R1,locked Gevaar op LiveLock
Synchronisatieprotocol met eindige wachttijd LOADI R1,1 SWAP R1,locked COMPARE R1,1 JNE OK “store draad_id” “call blokkeer” OK: LOADI R1,1 SWAP R1,locked COMPARE R1,1 JNE OK “store draad_id” “call blokkeer” OK: kritieke sectie 2 kritieke sectie 1 Indien geblokkeerde draad deblokkeer oudste draad anders LOADI R1,0 SWAP R1,locked Indien geblokkeerde draad deblokkeer oudste draad anders LOADI R1,0 SWAP R1,locked
Overzicht • Wat is synchronisatie? • Software-oplossingen • Hardware-oplossingen • Semafoor • Monitor • Transactioneel geheugen • Boodschappen • Impasses Edsger Wybe Dijkstra (1930– 2002)
Semafoor s=1 acquire(s) acquire(s) kritieke sectie 2 kritieke sectie 1 release(s) release(s)
Implementatie binaire semafoor LOADI R1,0 L: SWAP R1,sema COMPARE R1,0 JE L kritieke sectie LOADI R1,1 STORE R1,sema wederzijds uitgesloten vooruitgang geen eindige wachttijd
Binaire semafoor vs. tellende semafoor • Tellende semafoor (Counting semaphore) • Geïnitialiseerd met een natuurlijk getal • Acquire() decrementeert; bij 0 wordt er geblokkeerd • Release() incrementeert • Binaire semafoor = tellende semafoor met initiële waarde = 1 • Mutex = binaire semafoor waarbij acquire() en release() door dezelfde draad moeten gebeuren
Gebruik Semaforen D1 D2 D1 D2 D1 D2 I+A I I A A R R A R A A R R Wederzijdse uitsluiting (mutex) Wachten
Problemen met semaforen • Ongestructureerd – kan leiden tot synchronisatiefouten zoals race condities en impasses.
Klassieke synchronisatieproblemen • Eindige buffer • Lezers-schrijversprobleem • Dinerende filosofen • Barrier
Eindige buffer met semaforen public class BoundedBuffer implements Buffer { private static final int BUFFER_SIZE = 5; private volatileObject[] buffer; private volatile int in, out; private volatileSemaphore insertmutex; private volatileSemaphore removemutex; private volatileSemaphore empty; private volatileSemaphore full;
Eindige buffer met semaforen empty = 3 full = 2 4 public BoundedBuffer() { // buffer is initially empty in = 0; out = 0; buffer = new Object[BUFFER_SIZE]; insertmutex = new Semaphore(1); removemutex = new Semaphore(1); empty = new Semaphore(BUFFER_SIZE); full = new Semaphore(0); } 3 out=0 0 2 1 in=2
Eindige buffer met semaforen public Object remove() { full.acquire(); removemutex.acquire(); // remove an item from the buffer Object item = buffer[out]; out = (out+1) % BUFFER_SIZE; removemutex.release(); empty.release(); return item; } public void insert(Object item) { empty.acquire(); insertmutex.acquire(); // add an item to the buffer buffer[in] = item; in = (in+1) % BUFFER_SIZE; insertmutex.release(); full.release(); }
Lezers-schrijversprobleem: Interface public interface RWLock { public abstract void acquireReadLock(); public abstract void acquireWriteLock(); public abstract void releaseReadLock(); public abstract void releaseWriteLock(); }
Dinerende filosofen Semaphore chopStick[] = new Semaphore[5];
Dinerende filosofen public void philosopher(int i) { while (true) { // get left chopstick chopStick[i].acquire(); // get right chopstick chopStick[(i + 1) % 5].acquire(); eating(); // return left chopstick chopStick[i].release(); // return right chopstick chopStick[(i + 1) % 5].release(); thinking(); } }
Overzicht • Wat is synchronisatie? • Software-oplossingen • Hardware-oplossingen • Semafoor • Monitor • Transactioneel geheugen • Boodschappen • Impasse
Monitor monitor monitor-name { // variable declarations procedure p1(…) { … } procedure p2(…) { … } }
Monitor Ingangssectie Ingangssectie insert(item) insert(item) Uitgangssectie Uitgangssectie
Eindige buffer met monitor Monitor BoundedBuffer implements Buffer { private static final int BUFFER_SIZE = 5; private volatileObject[] buffer; private volatileint in, out, count; public BoundedBuffer() { in = 0; out = 0; count = 0; buffer = new Object[BUFFER_SIZE]; } public void insert(Object item) { if (count < BUFFER_SIZE) { count++; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; } else raise Exception.Create(“buffer vol”); } public Object remove() { if (count > 0) { Object item = buffer[out]; count--; out = (out + 1) % BUFFER_SIZE; return item; } else raise Exception.Create(“buffer leeg”); } } In pseudo-Java stijl
Conditieveranderlijken condition x; x.wait() x.signal()
Eindige buffer met monitor en condities Monitor BoundedBuffer implements Buffer { private static final int BUFFER_SIZE = 5; private volatileObject[] buffer; private volatile int in, out, count; private volatile Condition notempty, notfull; public BoundedBuffer() { in = 0; out = 0; count = 0; buffer = new Object[BUFFER_SIZE]; notempty = new Condition; notfull = new Condition; } public void insert(Object item) { while (count == BUFFER_SIZE) notfull.wait(); count++; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; if (count == 1) notempty.signal(); } public Object remove() { Object item; while (count == 0) notempty.wait(); count--; item = buffer[out]; out = (out + 1) % BUFFER_SIZE; if (count == BUFFER_SIZE-1) notfull.signal(); return item; } } In pseudo-Java stijl
Eindige buffer met Semaforen & condities public class BoundedBuffer implements Buffer { private static final int BUFFER_SIZE = 5; private volatileObject[] buffer; private volatileint in, out, count; private volatile Condition notempty, notfull; private volatile Semaphore mutex; public BoundedBuffer() { in = 0; out = 0; count = 0; buffer = new Object[BUFFER_SIZE]; notempty = new Condition; notfull = new Condition;mutex = new Semaphore(1); } public void insert(Object item) { mutex.acquire(); while (count == BUFFER_SIZE) { mutex.release(); notfull.wait(); mutex.acquire(); } count++; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; if (count == 1) notempty.signal(); mutex.release(); } public Object remove() { Object item; mutex.acquire(); while (count == 0) { mutex.release(); notempty.wait(); mutex.acquire(); } count--; item = buffer[out]; out = (out + 1) % BUFFER_SIZE; if (count == BUFFER_SIZE-1) notfull.signal(); mutex.release(); return item; } } Lost signal race notfull.signal() Niet persistent
Eindige buffer met semaforen en condities public class BoundedBuffer implements Buffer { private static final int BUFFER_SIZE = 5; private volatileObject[] buffer; private volatileint in, out, count; private volatile Condition notempty, notfull; private volatile Semaphore mutex; public BoundedBuffer() { in = 0; out = 0; count = 0; buffer = new Object[BUFFER_SIZE]; notempty = new Condition; notfull = new Condition; mutex = new Semaphore(1); } public void insert(Object item) { mutex.acquire(); while (count == BUFFER_SIZE) notfull.wait(mutex); count++; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; if (count == 1) notempty.signal; mutex.release(); } public Object remove() { Object item; mutex.acquire(); while (count == 0) notempty.wait(mutex); count--; item = buffer[out]; out = (out + 1) % BUFFER_SIZE; if (count == BUFFER_SIZE-1) notfull.signal; mutex.release(); return item; } }
Semaforen + wait/signal s=1 acquire(s) acquire(s) ... cond.wait(s) ... ... cond.signal() ... release(s) release(s)
Eindige buffer met Java synchronisatie public class BoundedBuffer implements Buffer { private static final int BUFFER_SIZE = 5; private volatileint count, in, out; private volatileObject[] buffer; public BoundedBuffer() {// buffer is initially empty count = 0; in = 0; out = 0; buffer = new Object[BUFFER_SIZE]; } publicsynchronizedvoid insert(Object item) { … } publicsynchronizedObject remove() { … } } intrinsieke lock
insert() met wait/notify methoden public synchronized void insert(Object item) { while (count == BUFFER_SIZE) { try { wait(); } catch (InterruptedException e) { } } ++count; buffer[in] = item; in = (in + 1) % BUFFER_SIZE; notify(); }
remove() met wait/notify methoden public synchronized Object remove() { Object item; while (count == 0) { try { wait(); } catch (InterruptedException e) { } } --count; item = buffer[out]; out = (out + 1) % BUFFER_SIZE; notify(); return item; }