Dagens tema: Vranglås De spisende filosofer Lettvektsprosesser Moderne synkroniseringsmetoder Meldinger Monitorer Linda Hvorfor nyttig? Parallellstyring brukes mye innen operativsystemer, systemprogrammer, kommunikasjon og nettverksprogrammer, databaser og programmer parallellisert av hensyn til hastigheten. Ark 1 av 27 Forelesning 3.5.2000
Vranglås Av og til kan det skje at flere prosesser venter på hverandre, for eksempel A skriver fil F A og trenger å lese F B. B skriver fil F B og trenger å lese F A. Hvis det å skrive på en fil låser den, har vi en vranglåssituasjon. Løsning Den eneste måten å løse opp en slik situasjon på er å drepe en av prosessene. Forhåpentligvis kan den startes på nytt igjen. Forelesning 3.5.2000 Ark 2 av 27
Hvordan løse vranglås? Dette har det vært forsket mye på. Noen muligheter er: Programmere slik at de ikke oppstår! Skrive programmene slik at de kan startes på nytt om noe går galt. Lære å leve med dem («strutsemetoden»). Forelesning 3.5.2000 Ark 3 av 27
Endelig: et forslag som fungerer Denne løsningen ble funnet av Edsger Dijkstra: #define N 5 #define LEFT(x) ((x)==0? N-1 : (x)-1) #define RIGHT(x) ((x)==n-1? 0 : (x)+1) enum Status {Tenker, Sulten, Spiser; enum Status stat[n]; semaphore mutex = 1; semaphore s[n]; void philosopher(i) /* Filosof nr. i */ int i; { while (TRUE) { think(); take_forks(i); eat(); put_forks(i); void take_forks(i) /* Få tak i 2 gafler. */ int i; { down(&mutex); /* Kritisk region... */ stat[i] = Sulten; /* Fortell om sulten. */ test(i); /* Prøv å få 2 gafler. */ up(&mutex); /*...kritisk region. */ down(&s[i]); /* Blokkér uten gafler. */ Forelesning 3.5.2000 Ark 4 av 27
void put_forks(i) /* Legg gaflene tilbake. */ int i; { down(&mutex); /* Kritisk region... */ stat[i] = Tenker; /* Fortell hva jeg gjør. */ test(left(i)); /* Sjekk om en nabo */ test(right(i)); /* vil spise nå. */ up(&mutex); /*...kritisk region. */ void test(i) /* Kan filosof nr i spise? */ int i; { if (stat[i] == Sulten && stat[left(i)]!= Spiser && stat[right(i)]!= Spiser) { stat[i] = Spiser; /* Nå kan nr. i spise. */ up(&s[i]); Forelesning 3.5.2000 Ark 5 av 27
Hvorledes fungerer dette opplegget? philosopher er filosof-prosessene. Hver filosof veksler mellom å tenke, bli sulten og ta opp to gafler (take forks vil blokkere inntil de aktuelle to gaflene er ledige), spise, og legge fra seg gaflene. take forks endrer filosofens status til Sulten. Så sjekkes (i funksjonen test) om filosofen kan spise; i så fall avsluttes rutinen; hvis ikke blokkeres på down(&s[i]). put forks vil legge fra seg gaflene og endre filosofens status til Tenker. Så sjekkes de to naboene; hvis de ønsket å spise og kan gjøre det nå, får de lov til det (up(&s[i])). test sjekker om en spesiell filosof (angitt som parameter) kan spise. I så fall endres status til Spiser, og det foretas en up(&s[i]). Avhengig av kallet kan dette ha to effekter: Hvis test ble kalt fra take forks, vil det siste down-kallet der gå glatt. Hvis test ble kalt fra put forks, vil den aktuelle filosofen være blokkert på det siste down-kallet i take forks, og vil nå få lov å spise. Forelesning 3.5.2000 Ark 6 av 27
Lettvektsprosesser Det er vanlig å tegne en prosess slik: Stakk Data Kode En lettvektsprosess (ofte kalt «tråd») deler data- og kodelager med opphavet: Stakk Stakk Data Kode Dataoverføring er da ikke lenger noe problem, men synkronisering er stadig nødvendig. Forelesning 3.5.2000 Ark 7 av 27
Lettvektsprosesser i Java I Java kan man opprette lettvektsprosesser ved å deklarere subklasser av Thread som inneholder en metode run: class PingPong extends Thread { String id; int ventetid; PingPong(String idx, int ventx) { id = idx; ventetid = ventx; public void run() { try { while (true) { System.out.print(id + " "); sleep(ventetid); catch (Exception e) { return; public static void main(string args[]) { new PingPong("ping", 33).start(); new PingPong("PONG", 84).start(); Metoden start() brukes til å starte tråden, og stop() til å stoppe den. maskin navn> java PingPong ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping PONG ping ping Forelesning 3.5.2000 Ark 8 av 27
Parallellstyring på høyt nivå Teknikkene beskrevet hittil har vært mellomnivåteknikker; de fungerer fint hvis programmeren behersker dem tilstrekkelig godt og ikke gjør feil. Feil bruk av semaforer semaphore mutex = 1; semaphore empty = N; /* Teller tomme */ semaphore full = 0; /* Teller fulle */ void producer() { int item; while (TRUE) { produce_item(&item); down(&mutex); down(&empty); /* Byttet! */ enter_item(item); up(&mutex); up(&full); void consumer() { int item; while (TRUE) { down(&full); down(&mutex); remove_item(&item); up(&mutex); up(&empty); consume_item(item); Forelesning 3.5.2000 Ark 9 av 27
Her kan følgende skje: 1. Vi antar at produsenten har vært raskere enn konsumenten slik at bufferen nå er full. 2. Produsenten lager enda ett element. Så utfører den down(&mutex) og går inn i kritisk region, og deretter down(&empty). Nå blokkeres den fordi bufferen er full. 3. Så skjer et prosessbytte. 4. Konsumenten gjør en down(&full) og deretter en down(&mutex) for å gå inn i kritisk region. Da blokkeres den, fordi produsenten allerede er i kritisk region. Vi har altså fått en situasjon med vranglås fordi to kall på down ble byttet om. Vi ønsker oss mer høynivå mekanismer hvor det er lettere å unngå slikt. Forelesning 3.5.2000 Ark 10 av 27
Meldinger Mange parallelle systemer er basert på en mekanisme som kalles meldinger. Det finnes mange varianter; én ser slik ut: Oversikt over meldinger Det finnes kun to operatorer for meldinger: send(q, kode, data ) sender meldingen data til meldingskøen q. Senderen blokkeren hvis køen er full, ellers ikke. receive(q, kode, & data ) henter en melding med angitt kode fra køen q. Henteren blokkeres inntil det finnes meldinger i køen. Forelesning 3.5.2000 Ark 11 av 27
Bruk av UNIX-meldinger #include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> typedef struct { long id; char data[200]; mess_struct; void barn(int n, int q) { mess_struct m; int r_stat; printf("dette er barn nr %d\n", n); r_stat = msgrcv(q, &m, sizeof(m), n, 0); printf("barn %d: status=%d, id=%d, data=\"%s\"\n", n, r_stat, m.id, m.data); Meldingene er av typen mess struct; legg merke til at koden ligger i første ord. Barne henter en melding med riktig kode (n) fra køen q (med msgrcv) og skriver ut data de får. maskin navn> q Dette er barn nr 1 Dette er barn nr 2 Barn 2: status=204, id=2, data="dette er post 2." Barn 1: status=204, id=1, data="dette er post 1." Forelesning 3.5.2000 Ark 12 av 27
int main(void) { mess_struct m1, m2; int q, status; q = msgget(ipc_private, 0700); /* Opprett køen */ if (q < 0) { perror("q"); exit(1); if (safefork() == 0) { barn(1,q); exit(0); if (safefork() == 0) { barn(2,q); exit(0); m1.id = 1; strcpy(m1.data, "Dette er post 1."); m2.id = 2; strcpy(m2.data, "Dette er post 2."); if (msgsnd(q,&m2,sizeof(m2),0)<0) { perror("q"); exit(12); if (msgsnd(q,&m1,sizeof(m1),0)<0) { perror("q"); exit(11); wait(&status); wait(&status); /* Vent på barna */ msgctl(q, IPC_RMID, NULL); /* Fjern køen */ Hovedprogrammet gjør følgende: 1. oppretter en meldingeskø q, 2. oppretter to barneprosesser, 3. bygger opp to meldinger m1 og m2, 4. sender de to meldingene til køen q, 5. venter til barna dør og 6. fjerner køen q. Forelesning 3.5.2000 Ark 13 av 27
Vurdering + Meldinger er enkle å bruke og forstå. + De er enkle å implementere. Resultatet er at meldinger brukes svært mye. Forelesning 3.5.2000 Ark 14 av 27
Monitorer Den aller første høynivåløsningen ble oppfunnetavc.a.r.hoareogper Brinch-Hansen i 1974. Den består av en klasse-lignende struktur kalt en monitor: class Eksempel { int i; // Lokal variabel. void synchronized p(...) {. wait();. notify();. // Operator: Eksempel e; e :- new Eksempel(); e.p(...); Forelesning 3.5.2000 Ark 15 av 27
Følgende gjelder for monitorer: Kun én prosess av gangen får utføre en operator i en monitor. Ytterligere forsøk på kall vil bli blokkert inntil monitoren er ledig. (I Java angis dette med synchronized.) Alle de lokale variablene er usynlige utenfor monitoren. (Dette kreves ikke i Java, men jeg anbefaler det.) En monitor kan blokkere seg selv ved å benytte wait() vil blokkere prosessen. Monitoren vil da bli åpnet for andre prosesser. notify() vil starte en eller annen prosess som tidligere ble blokkert ved å utføre wait(). Forelesning 3.5.2000 Ark 16 av 27
PK-problemet med Javas monitorer Produsenten class Producer extends Thread { Monitor m; intx,i,s,n; Producer(int start, int inkr, int sleep, Monitor mx) { x = start; i = inkr; s = sleep; m = mx; n = 0; public void run() { while (n < 20) { m.enter(x); x += i; ++n; try { sleep(s); catch (Exception e) { return; Produsenten er en lettvektsprosess som genererer 20 tall x, x + i,x + 2i,...,x+19i som sendes til monitoren med m.enter(x). Etter hvert tall sover prosessen litt. Forelesning 3.5.2000 Ark 17 av 27
Konsumenten class Consumer extends Thread { Monitor m; Consumer(Monitor mx) { m = mx; setdaemon(true); public void run() { int n_on_line = 0; while (true) { int x = m.remove(); System.out.print(x + " "); if (++n_on_line%10 == 0) System.out.println(""); Konsumenten er en lettvektsprosess som henter tall fra monitoren med m.remove() og skriver dem ut, 10 på hver linje. Konsumenten er konfigurert som demon slik at den automatisk drepes når alle brukerprosessene er ferdige. Forelesning 3.5.2000 Ark 18 av 27
Monitoren (med hovedprogrammet) class Monitor { // En ringbuffer: private int buf[], inn_p = 0, ut_p = 0, n = 0; Monitor() { buf = new int[10]; public synchronized void enter(int x) { try { while (n == 10) wait(); catch (Exception e) { System.exit(1); buf[inn_p++] = x; ++n; if (inn_p >= 10) inn_p = 0; notify(); public synchronized int remove() { int res; try { while (n == 0) wait(); catch (Exception e) { System.exit(2); res = buf[ut_p++]; --n; if (ut_p >= 10) ut_p = 0; notify(); return res; public static void main(string args[]) { Monitor m = new Monitor(); new Producer(1,2,25,m).start(); new Producer(101,1,40,m).start(); new Consumer(m).start(); Forelesning 3.5.2000 Ark 19 av 27
Hoveprogrammet Hovedprogrammet main oppretter en monitor. Så oppretter den to produsenter og én konsument som startes. Monitoren Monitoren har en privat ringbuffer til bufring av 10 tall. Den har to operatorer: enter venter først til det er plass i ringbufferen. Så setter den inn et element og kaller notify (i tilfelle en konsument venter). remove venter først til det er noen elementer i ringbufferen. Så henter den ett element derfra som skal returneres. Først kaller den imidlertid notify (i tilfelle noen produsenter venter). Siden ringbufferen er private og operatorene er synchronized, er parallellstyringen sikker. Kjøring Slik blir en kjøring: maskin navn> java Monitor 1 101 3 102 5 103 7 9 104 11 105 13 15 106 17 107 19 21 108 23 109 25 27 110 29 111 31 33 112 35 113 37 39 114 115 116 117 118 119 120 Forelesning 3.5.2000 Ark 20 av 27
Vurdering + Monitorer er meget sikre. Kompilatoren vil ta seg av det meste av parallellitetskontrollen. + De er enkle å bruke. Det er lettere å unngå vranglås. + Det er trivielt å overføre data mellom prosessene (ved å benytte parametre til monitor-operatorene). + De gir anledning til strukturert programmering, siden monitorer er en form for abstrakte typer. Monitorer krever et eget språk; det er ikke nok å skrive noen rutiner. Svært få språk har monitorer. Konklusjon Monitorer er ypperlige, hvis de finnes. Forelesning 3.5.2000 Ark 21 av 27
Linda Linda er et av de aller nyeste konseptene for prosesskommunikasjon. Det er basert på tanken om at det finnes et eget lager for tupler hvor prosessene kan lagre og lese tupler. Et tuppel er en datastruktur med vilkårlig mange elementer; i Linda er hvert element er enten en verdi (av en eller annen type) eller en «potensiell verdi». Eksempler er (2, 4) ("Test", 4, 3) ("Melding",?x) (Her er?x er potensiell verdi.) Forelesning 3.5.2000 Ark 22 av 27
Følgende atomiske operasjoner finnes i Linda: out(t 1,...,t n )beregner alle tuppelelementene og legger det ferdige tuppelet i det felles tuppel-lageret. eval(t 1,...,t n )fungerer som out, men tuplene beregnes i parallell etter at tuppel er lagret. Mens beregningene foregår kalles tuppelet et aktivt tuppel. in(t 1,...,t n )henter et tuppel som «passer»; prosessen blokkeres inntil et slik tuppel eksistrerer. rd(t 1,...,t n )fungerer som in, men lar kopi av tuppelet bli liggende i lageret. Forelesning 3.5.2000 Ark 23 av 27
Operasjonene in og rd leter etter tupler som passer med det som er oppgitt som parameter. Et tuppel passer hvis det er passivt, har like mange elementer, hvert tilsvarende element er av samme type, og hvert tilsvarende element har samme verdi, eller det ene er en potensiell verdi. Enkel bruk av Linda Følgende lille program vil beregne kvadratroten av tre tall i parallell: float a, b, c;. eval("kvadrot",sqrt(9.0),sqrt(16.0),sqrt(25.0)); in ("Kvadrot",?a,?b,?c); Forelesning 3.5.2000 Ark 24 av 27
Dette vil skje: 1. Først vil kallet på eval legge det aktive tuppelet (med 4 elementer) i tuppel-lageret. 2. Så startes 4 nye prosesser som vil gå parallelt for å beregne de fire parametrene. 3. Når alle beregningene er gjort, registreres tuppelet som passivt. 4. Kallet på in vil lete etter tupler med 4 elementer: en tekst og 3 float-verdier; teksten skal ha verdien "Kvadrot". (Hvis dette kallet kommer før beregningene av de tre kvadratrøttene er ferdig, blir det blokkert inntil alt er klart.) 5. Resultatene kopieres over i variablene: a blir 3.0, b blir 4.0 og c blir 5.0. Forelesning 3.5.2000 Ark 25 av 27
Beskyttelse av kritisk region Følgende enkle teknikk kan benyttes til å beskytte en kritisk region: out("kritreg1"); /* Initiering */. in("kritreg1"); Kritisk region 1 out("kritreg1");. Oppsummering + Rimelig enkelt å implementere (men ikke så enkelt som meldinger). + Lett å bruke, men allikevel kraftig. Ideene om ulikt antall parametre og potensielle verdier kan skape problemer vis-a-vis programmeringsspråket. Ennå ikke veldig utbredt. Forelesning 3.5.2000 Ark 26 av 27
Oppsummering av teknikker Teknikk UNIX filer Synk Data Spesielt UNIX rør Forutsetter samme urprosess Delt lager Lettvektsprosesser Semaforer Meldinger Monitorer Lite brukt Linda Ennå lite brukt Forelesning 3.5.2000 Ark 27 av 27