Ohjelmoinnin peruskurssien laaja oppimäärä



Samankaltaiset tiedostot
Ohjelmoinnin peruskurssien laaja oppimäärä

Luento 6. T Ohjelmoinnin jatkokurssi T1 & T Ohjelmoinnin jatkokurssi L1. Luennoitsija: Otto Seppälä

Liite 1. Projektin tulokset (Semaforit Javassa) Jukka Hyvärinen Aleksanteri Aaltonen

Projekti 1 Säikeet ja kriittisen vaiheen kontrollointi javalla

Rinnakkaisuus. parallel tietokoneissa rinnakkaisia laskentayksiköitä concurrent asioita tapahtuu yhtaikaa. TTY Ohjelmistotekniikka

5. Luento: Rinnakkaisuus ja reaaliaika. Tommi Mikkonen,

Rinnakkaisohjelmointi kurssi. Opintopiiri työskentelyn raportti

Rinnakkaisohjelmointi, Syksy 2006

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

812315A Ohjelmiston rakentaminen. Asynkronisuus

HOJ Säikeet (Java) Ville Leppänen. HOJ, c Ville Leppänen, IT, Turun yliopisto, 2012 p.1/55

Transaktiot - kertausta

Monitorit -projekti Rinnakkaisohjelmointi

Käyttöjärjestelmät: poissulkeminen ja synkronointi

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

4. Luokan testaus ja käyttö olion kautta 4.1

Monitorit. Monitori Synkronointimenetelmiä Esimerkkejä. Andrews , Stallings 5.5

Monitorit. Tavoite. Monitori Synkronointimenetelmiä Esimerkkejä. Andrews , Stallings 5.5. Minimoi virhemahdollisuuksia

11/20: Konepelti auki

Javan semaforit. Joel Rybicki, Aleksi Nur mi, Jara Uitto. Helsingin yliopisto

Javan perusteita. Janne Käki

Chapel. TIE Ryhmä 91. Joonas Eloranta Lari Valtonen

Listarakenne (ArrayList-luokka)

4. Luento: Prosessit ja säikeets. Tommi Mikkonen,

8. Näppäimistöltä lukeminen 8.1

815338A Ohjelmointikielten periaatteet

Ohjelmoinnin jatkokurssi, kurssikoe

8. Näppäimistöltä lukeminen 8.1

Rajapinta (interface)

Graafisen käyttöliittymän ohjelmointi Syksy 2013

Sisältö. 2. Taulukot. Yleistä. Yleistä

1. Olio-ohjelmointi 1.1

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Samanaikaisuuden hallinta

Sisällys. 12. Näppäimistöltä lukeminen. Yleistä. Yleistä

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä ja ulkopuolelta. Attribuuttien arvojen käsittely aksessoreilla. 4.2

Metodit. Metodien määrittely. Metodin parametrit ja paluuarvo. Metodien suorittaminen eli kutsuminen. Metodien kuormittaminen

Ohjelmoinnin perusteet Y Python

1. Omat operaatiot 1.1

Sisältö. 22. Taulukot. Yleistä. Yleistä

5. HelloWorld-ohjelma 5.1

1 Tehtävän kuvaus ja analysointi

Rinnakkaisuuden hyväksikäyttö peleissä. Paula Kemppi

Vertailulauseet. Ehtolausekkeet. Vertailulauseet. Vertailulauseet. if-lauseke. if-lauseke. Javan perusteet 2004

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Jaana Diakite Projekti 1 JAVA-Monitorit 1(13) Rinnakkaisohjelmointi Anu Uusitalo

Pakkauksen kokoaminen

Palvelut. Sulautetut järjestelmät Luku 2 Sivu 1 (??) Sulautetut käyttöjärjestelmät

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op. Poikkeukset ja tietovirrat: Virhetilanteiden ja syötevirtojen käsittely

Ohjelmoinnin perusteet Y Python

Ohjelmointi 1 / 2009 syksy Tentti / 18.12

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Ohjelmointi 2 / 2010 Välikoe / 26.3

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

Harjoitustyö: virtuaalikone

1. Mitä tehdään ensiksi?

15. Ohjelmoinnin tekniikkaa 15.1

Mikä yhteyssuhde on?

Interaktiivinen tarinankerronta

Java kahdessa tunnissa. Jyry Suvilehto

12. Näppäimistöltä lukeminen 12.1

9. Periytyminen Javassa 9.1

Sisällys. Metodien kuormittaminen. Luokkametodit ja -attribuutit. Rakentajat. Metodien ja muun luokan sisällön järjestäminen. 6.2

Olio-ohjelmointi Javalla

Oliosuunnitteluesimerkki: Yrityksen palkanlaskentajärjestelmä

ITKP102 Ohjelmointi 1 (6 op)

Tehtävä 1. Tehtävä 2. Arvosteluperusteet Koherentti selitys Koherentti esimerkki

RINNAKKAINEN OHJELMOINTI A,

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 15.3

7. Oliot ja viitteet 7.1

Sisällys. 15. Lohkot. Lohkot. Lohkot

Yksikkötestaus. import org.junit.test; public class LaskinTest public void testlaskimenluonti() { Laskin laskin = new Laskin(); } }

Sisältö Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 26.2

Kompositio. Mikä komposition on? Kompositio vs. yhteyssuhde Kompositio Javalla Konstruktorit set-ja get-metodit tostring-metodi Pääohjelma

Java-kielen perusteet

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

Metodien tekeminen Javalla

11. Javan toistorakenteet 11.1

16. Javan omat luokat 16.1

Lohkot. if (ehto1) { if (ehto2) { lause 1;... lause n; } } else { lause 1;... lause m; } 16.3

Ohjelmointi 2 / 2011 Välikoe / 25.3

Sisällys. JAVA-OHJELMOINTI Osa 7: Abstrakti luokka ja rajapinta. Abstraktin luokan idea. Abstrakti luokka ja metodi. Esimerkki

Sisältö. Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 6.2

Ehto- ja toistolauseet

Sisällys. 6. Metodit. Oliot viestivät metodeja kutsuen. Oliot viestivät metodeja kutsuen

Sisällys. 11. Javan toistorakenteet. Laskurimuuttujat. Yleistä

Koottu lause; { ja } -merkkien väliin kirjoitetut lauseet muodostavat lohkon, jonka sisällä lauseet suoritetaan peräkkäin.

Ohjelmoinnin peruskurssien laaja oppimäärä

private TreeMap<String, Opiskelija> nimella; private TreeMap<String, Opiskelija> numerolla;

2 Konekieli, aliohjelmat, keskeytykset

Sisältö. Johdanto. Tiedostojen lukeminen. Tiedostojen kirjoittaminen. 6.2

7. Näytölle tulostaminen 7.1

9. Periytyminen Javassa 9.1

15. Ohjelmoinnin tekniikkaa 15.1

Ohjelmistojen mallintaminen viikon 4 laskareiden mallivastauksia

Luokat ja oliot. Ville Sundberg

Oppimistavoitteet kurssilla Rinnakkaisohjelmointi

Ohjelmoinnin perusteet Y Python

Transkriptio:

Ohjelmoinnin peruskurssien laaja oppimäärä Luento 19: Rinnakkaisuus Riku Saikkonen (merkityt ei-laajan kurssin kalvot: Otto Seppälä) 24. 3. 2011

Sisältö 1 Säikeet 2 Lukot 3 Monitorit 4 Muuta säikeisiin liittyvää 5 Rinnakkaisuuden ja säiemallin ongelmia 6 Muita rinnakkaisuusmalleja

(ei-laajan kurssin kalvo) Mikä on Säie? Prosessi Kaikissa nykyaikaisissa käyttöjärjestelmissä on mahdollista suorittaa useita ohjelmia yhtä aikaa Käyttöjärjestelmä ajaa jokaista ohjelmaa omassa prosessissaan, jakaen prosessoriaikaa ohjelmille niin, että ohjelmat näyttävät toimivan samanaikaisesti Säie Yhden prosessin sisällä myös yksittäinen ohjelma voi suorittaa eri toimintoja samanaikaisesti käyttämällä säikeitä (thread) Säikeet ovat prosesseihin verrattuna selkeästi kevyempiä 21:32

(ei-laajan kurssin kalvo) Mikä on säie? Sopivasti käytettynä säikeillä saadaan paljon etuja ohjelma voi käyttää prosessoriaikaa mahdollisimman tehokkaasti hyväkseen ohjelma voi vastata käyttäjän toimiin nopeammin jne. Jokaisella säikeellä on oma suorituspino paikallisia muuttujia (suorituspinossa) ei jaeta kaikki muu on yhteistä (keossa) Java tukee rinnakkaisohjelmointia jo kielen tasolla Selkiyttää asioita Monessa ohjelmointikielessä tuki säikeille toteutetaan jonkin ohjelmointikirjaston avulla 21:32

(ei-laajan kurssin kalvo) Luokka Thread Luokka Thread mahdollistaa säikeiden ohjaamisen sleep() pysäyttää säikeen halutuksi ajaksi interrupt() keskeyttää odotuksen start() käynnistää säikeen isalive() kertoo onko säie elossa join() odottaa säikeen kuolemista yield() antaa suoritusvuoron muille getname() kertoo säikeen nimen getpriority() kertoo säikeen prioriteetin setpriority() asettaa säikeen prioriteetin currentthread() antaa ajossaolevan säikeen 21:32

(ei-laajan kurssin kalvo) Sleep Allaoleva ohjelma tulostaa sanan Testi kolme kertaa noin kolmen sekunnin välein public class Esimerkki { public static void main ( String[] args) { try { for (int i=0; i<3; i++) { System.out.println( Testi ); Thread.sleep(3000); catch (InterruptedException e) { 21:32

(ei-laajan kurssin kalvo) Sleep Sleepin parametri kertoo kuinka kauan säie on pysähdyksissä (millisekunneissa) HUOM! Tämä on minimiaika. Säikeen paluu ajoon voi kestää pidempään. (Älä rakenna kelloa sleep-metodilla) Mitä muuta esimerkissä tapahtui? Sleep-metodin kutsu oli sijoitettu try-catch rakenteen sisään, koska säikeen nukkuminen voidaan keskeyttää kutsumalla sen interruptmetodia 21:32

(ei-laajan kurssin kalvo) Säikeen luonti ja käynnistys Kuinka säie luodaan? Uusi säie voidaan luodaan luomalla uusi Thread-olio ja kutsumalla sen start-metodia Start käynnistää säikeen halutusta aloituskohdasta Ennen start()-metodin kutsua säie on pysähdyksissä Tämä aloituspiste voidaan antaa kahdella tavalla toteuttamalla rajapinta Runnable periyttämällä uusi luokka luokasta Thread Lopputulos on molemmissa tapauksissa sama suoritus alkaa jommankumman metodista run() run()-metodia ei kutsuta suoraan vaan Thread tekee sen kun sitä vastaava säie käynnistetään start:illa 21:32 säikeen suoritus päättyy kun run()-metodista poistutaan

(ei-laajan kurssin kalvo) Runnable rajapinta public class Tulostaja implements Runnable { private String viesti; public Tulostaja( String tulostatama ) { this.viesti = tulostatama public void run () { try { for (int i=0; i<10; i++) { Thread.sleep(500); System.out.println(this.viesti + i); catch (InterruptedException e) { 21:32

(ei-laajan kurssin kalvo) Runnable rajapinta public class Esimerkki2 { public static void main ( String[] args){ Tulostaja t = new Tulostaja( Moi : ); Tulostaja u = new Tulostaja( Hei : ); Thread saiea = new Thread(t); Thread saieb = new Thread(u); saiea.start(); saieb.start(); try { for (int i=0; i<10; i++) { Thread.sleep(1000); System.out.println( Main + i); catch (InterruptedException e) { 21:32

(ei-laajan kurssin kalvo) Muutama muu metodi isalive() tiedustelee, onko tutkittu säie elossa join() odottaa kunnes säie, jonka join-metodia kutsuttiin kuolee Voidaan vaikkapa odottaa, että jonkin olennaisen tehtävän suoritus saadaan loppuun Voi olla kätevä ohjelman alasajossa Huomaa, että join() ei lopeta säikeitä vaan odottaa että ne kuolevat luonnollisesti yield() Tällä-hetkellä ajossa oleva säie luovuttaa ajovuoronsa muille 21:32

Miten säikeitä ajetaan? yleensä käyttöjärjestelmä toteuttaa säikeet useampi säie voi olla yhtäaikaa käynnissä esim. saman koneen eri prosessoreissa yhdelläkin prosessorilla säikeitä ajetaan vuorotellen joskus (nykyään harvoin) kieli itse toteuttaa säikeet ajamalla niitä vuorotellen samassa ohjelmassa säikeen suoritus voi keskeytyä mistä tahansa kohdasta (ja siirtyä toiseen säikeeseen) melkein: yleensä järjestelmä takaa esim. että Javan int-muuttujan kirjoitus on muiden säikeiden näkökulmasta tehty joko kokonaan tai ei vielä ollenkaan säikeiden välinen kommunikointi perustuu siis yhteisen muistin käyttämiseen (shared memory) ja käytännössä säikeiden synkronointiin esimerkiksi lukoilla

Sisältö 1 Säikeet 2 Lukot 3 Monitorit 4 Muuta säikeisiin liittyvää 5 Rinnakkaisuuden ja säiemallin ongelmia 6 Muita rinnakkaisuusmalleja

(ei-laajan kurssin kalvo) Synkronointi Kun kaksi säiettä tai useampi säie haluaa käyttää jotakin yhteistä resurssia (muuttuja, tiedosto, jne) yhtäaikaisesti, täytyy pitää huolta että vain yksi säie kerrallaan käsittelee resurssia. Esim tilanne jossa kaksi säiettä avaavat saman tiedoston ja kirjoittavat siihen. Menevätkö kirjoitetut rivit sekaisin? Toimintaa, joka takaa tämän yksi kerrallaan - periaatteen kutsutaan synkronoinniksi 21:32

Lukko-operaatiot (SICP 3.4.2) lukot (lock tai mutex) ovat usein matalimman abstraktiotason primitiivi säikeiden synkronointiin tyypillisesti lukolla on kaksi operaatiota: jokin säie voi varata (acquire) lukon ja myöhemmin vapauttaa (release) sen lukko takaa, että se on varattuna vain yhdellä säikeellä kerrallaan jos yrittää varata lukon, joka on jo varattu, normaalisti varausoperaatio jää odottamaan, että lukko vapautuu sisäisesti lukot on toteutettu joko erikseen (käyttöjärjestelmän avulla) tai yhteistä muistia sopivasti muokkaamalla semafori (semaphor) on lukko, joka voi olla varattuna n 1:llä säikeellä kerrallaan (raja n kerrotaan etukäteen)

Lukot ja monitorit Javassa on sisäänrakennettuna rinnakkaisuusabstraktio nimeltä monitori se käyttää lukkoja, mutta tarjoaa niille hieman abstraktimman rajapinnan: yksittäisten lukkojen varaus- ja vapautusoperaatioita ei näy koodissa lukot, semaforit ja monitorit ovat yleisimmät jaettuun muistiin perustuvat synkronointitavat oikeastaan abstraktiohierarkia ei ole näin selkeä: kaikki kolme voi periaatteessa toteuttaa toistensa avulla lisäksi on muunlaisia rinnakkaisuusmalleja, esimerkiksi viestinvälitykseen perustuvia (luennon loppupuolella)

Sisältö 1 Säikeet 2 Lukot 3 Monitorit 4 Muuta säikeisiin liittyvää 5 Rinnakkaisuuden ja säiemallin ongelmia 6 Muita rinnakkaisuusmalleja

(ei-laajan kurssin kalvo) Monitori Javassa synkronointi toteutetaan ns. monitorin avulla Monitori on jokin alue ohjelmakoodista, joka suojataan lukkoolion avulla - Mikä tahansa Java:n olio voi toimia lukkona Vain yksi säie kerrallaan voi olla ajossa monitorin sisällä jos jokin säie on jo ajossa monitorin sisällä, muut säikeet odottavat ulkopuolella kunnes sisällä oleva säie poistuu monitorista säie voi poistua monitorista joko niin, että se poistuu koodialueelta normaalisti tai siirtymällä odottamaan kutsumalla metodia wait() Monitorin alue rajataan synchronized-määreen avulla 21:32

(ei-laajan kurssin kalvo) Synchronized Synchronized-määreellä on kaksi käyttötapaa Määre voidaan kirjoittaa metodin eteen, jolloin se olio, jonka metodia kutsutaan toimii monitorin lukkona public class Posti { public synchronized Paketti otapaketti() { // jotain koodia public synchronized void tuopaketti( Paketti paketti ){ // jotain koodia Annetussa esimerkissä Posti-olio toimii itse lukkona, siten, että vain yksi säie kerrallaan voi suorittaa jotakin luokasta löytyvää synkronoitua metodia. paketteja ei siis voi tuoda samanaikaisesti, viedä samanaikaisesti taikka tuoda ja viedä yhtä aikaa, jolloin kukaan ei mm. voi viedä 21:32 samaa pakettia kahdesti

(ei-laajan kurssin kalvo) Synchronized Toinen käyttötapa synchronized määreelle on kirjoittaa synkronoitu lohko, jonka yhteydessä määritetään olio, joka toimii lukkona public class Posti { ArrayList lahtevatpaketit; public Paketti otapaketti() { // jotain koodia synchronized( lahtevatpaketit ) { // koodia joka käsittelee pakettien jonoa // jotain koodia Tässä lahtevatpaketit on lukko, joka estää että synkronoituun lohkoon pääsisi useampi säie kerrallaan 21:32

(ei-laajan kurssin kalvo) wait() ja notify() Entäs jos postissa ei olekaan paketteja? Tietenkin voitaisiin kirjoittaa silmukka joka käy hakemassa pakettia kunnes saa sellaisen Käytännössä tämä veisi kaikki järjestelmän suoritustehot turhan silmukan suoritukseen Ongelma ratkeaa metodeilla wait() ja notify() Kutsumalla lukko-olion metodia wait() säie poistuu monitorista ja siirtyy odottamaan Kun joku toinen säie kutsuu lukko-olion metodia notify(), se herättää yhden wait-jonon säikeen odottamaan pääsyä takaisin suoritukseen wait-kutsua seuraavalta riviltä Kaikki wait-jonon säikeet voi siirtää ready-jonoon komennolla notifyall() 21:32

(ei-laajan kurssin kalvo) public class Posti { private ArrayList<Paketti> paketit = new ArrayList<Paketti>(); public static final int POSTIN_KOKO = 15; public synchronized Paketti otapaketti() { while( paketit.isempty() ) { wait(); //Jos paketteja ei ole, odotetaan Paketti poistettava = paketit.remove(0); notifyall(); //syntyi tilaa, joku odottaja voisi toimia //ylimääräiset herääjät //siirtyvät whilen ansiosta takaisin nukkumaan return poistettava; public synchronized void tuopaketti( Paketti p ) { while( paketit.size() >= POSTIN_KOKO ) { wait(); //Jos posti on täysi, odotetaan paketit.add( p ); notifyall(); //tuli haettavaa, joku odottaja voi toimia 21:32

(ei-laajan kurssin kalvo) Javan monitori ajovalmiit säikeet Aina kun monitorissa ei ole ajossa säiettä, jokin ajovalmis säie voi mennä monitoriin Monitori synkronoitu metodi monitorissa oleva säie siirtää yhden odottavan säikeen ajovalmiuteen kutsumalla notify() notify Monitorissa juuri nyt oleva säie synkronoitu metodi odottavat säikeet wait synkronoitu metodi Monitorissa oleva säie siirtää itsensä odottamaan kutsumalla wait() 21:32

(ei-laajan kurssin kalvo) avohärdelli (ei jono) 21:32

Sisältö 1 Säikeet 2 Lukot 3 Monitorit 4 Muuta säikeisiin liittyvää 5 Rinnakkaisuuden ja säiemallin ongelmia 6 Muita rinnakkaisuusmalleja

(ei-laajan kurssin kalvo) Volatile Säikeet voivat tehokkuussyistä käyttää väliaikaisesti omia kopioita joistakin yhteisistä muuttujista Mikäli on erityisen tärkeää että jonkin muuttujan tila on yksikäsitteinen, kirjoitetaan muuttujan määrittelyyn sana volatile Tämä pakottaa javan käyttämään vain yhtä kopiota muuttujasta Samaan päästään luonnollisesti myös sopivalla synkronoinnilla Huom: volatile ei ole tapa toteuttaa synkronointia 21:32

Esimerkki rinnakkaisuudesta ilman lukkoja (SICP 3.4.2) Kaksi rinnakkain ajettavaa lauseketta x = x * x; x = x + 1; int-tyyppinen muuttuja x on alun perin 10 mitä arvoja x:llä voi olla kun molemmat lausekkeet on suoritettu, jos lausekkeiden osia ei voi ajaa rinnakkain (koodi alla)? entä jos rinnakkaisuutta ei rajoiteta synchronizedilla? Rinnakkaisuuden rajoittaminen synchronized (lock) { x = x * x; synchronized (lock) { x = x + x; // tai sama Scalassa: lock.synchronized { x = x * x lock.synchronized { x = x + 1

Esimerkki rinnakkaisuudesta ilman lukkoja (SICP 3.4.2) Kaksi rinnakkain ajettavaa lauseketta x = x * x; x = x + 1; int-tyyppinen muuttuja x on alun perin 10 mitä arvoja x:llä voi olla kun molemmat lausekkeet on suoritettu, jos lausekkeiden osia ei voi ajaa rinnakkain (koodi alla)? 101 121 entä jos rinnakkaisuutta ei rajoiteta synchronizedilla? Rinnakkaisuuden rajoittaminen synchronized (lock) { x = x * x; synchronized (lock) { x = x + x; // tai sama Scalassa: lock.synchronized { x = x * x lock.synchronized { x = x + 1

Esimerkki rinnakkaisuudesta ilman lukkoja (SICP 3.4.2) Kaksi rinnakkain ajettavaa lauseketta x = x * x; x = x + 1; int-tyyppinen muuttuja x on alun perin 10 mitä arvoja x:llä voi olla kun molemmat lausekkeet on suoritettu, jos lausekkeiden osia ei voi ajaa rinnakkain (koodi alla)? 101 121 entä jos rinnakkaisuutta ei rajoiteta synchronizedilla? 101 121 110 11 100 Rinnakkaisuuden rajoittaminen synchronized (lock) { x = x * x; synchronized (lock) { x = x + x; // tai sama Scalassa: lock.synchronized { x = x * x lock.synchronized { x = x + 1

Säikeet ja GUI-ohjelmointi Javassa/Scalassa Javan AWT ja Swing (ja useimmat muutkin GUI-kirjastot) on tehty niin, että GUI-kirjaston kutsuja saa tehdä vain yhdestä säikeestä helpottaa GUI-kirjaston tekemistä ja sen rajapinnan dokumentointia (muuten pitäisi ottaa kantaa siihen, mitä operaatioita saa tehdä yhtäaikaa ja mitä ei) mutta käyttöliittymässä asiat tapahtuvat yksi kerrallaan (hidas käyttöliittymäoperaatio pysäyttää kaiken muun) yleensä käyttöliittymäoperaatiot (esim. uuden dialog-ikkunan luonti) ovat kuitenkin lyhyitä tai osa niistä tehdään etukäteen ohjelman käynnistyessä käytännössä ohjelmissa on yleensä kaikki käyttöliittymään liittyvä koodi yhdessä säikeessä, joka lähettää pitkistä operaatioista laskentapyyntöjä muille säikeille

Sisältö 1 Säikeet 2 Lukot 3 Monitorit 4 Muuta säikeisiin liittyvää 5 Rinnakkaisuuden ja säiemallin ongelmia 6 Muita rinnakkaisuusmalleja

Säikeiden ongelmia lukituksessa on helppo tehdä virheitä (esim. unohtaa varata lukko) vika saattaa esiintyä harvoin, jolloin se löytyy vasta myöhemmin rinnakkaisuusbugeja on yleensä vaikea toistaa löydetyn virhetilanteen tutkiminenkin voi olla vaikeaa lukkoja ei kannata olla kovin paljon lukon varaaminen vie aikaa vaikka se olisi vapaa (varaamisen pitää useimmiten onnistua, muuten rinnakkaisuudesta ei ole hyötyä) pitää löytää tasapaino tarkan ja epätarkan lukituksen välillä säikeitä ei kannata olla kovin paljon useimmat säietoteutukset eivät ole kovin, jos säikeitä on paljon yleensä ratkaisuna on ns. säieallas (thread pool): käynnistetään etukäteen esim. vakiomäärä säikeitä ja lähetetetään niille (yleensä lyhyitä) laskentapyyntöjä uuden säikeen käynnistämisen sijaan useimmissa ohjelmissa on alle 10 säiettä, harvoin yli 20 monitorit ja semaforit eivät juurikaan auta näihin ongelmiin

Deadlock-ongelma (SICP 3.4.2) Kaksi rinnakkain ajettavaa säiettä varaa lukko a. varaa lukko b. vapauta b vapauta a varaa lukko b. varaa lukko a. vapauta a vapauta b mitä erikoista näitä säikeitä ajettaessa voi tapahtua?

Deadlock-ongelma (SICP 3.4.2) Kaksi rinnakkain ajettavaa säiettä varaa lukko a. varaa lukko b. vapauta b vapauta a varaa lukko b. varaa lukko a. vapauta a vapauta b mitä erikoista näitä säikeitä ajettaessa voi tapahtua? ohjelmat voivat jäädä ikuisesti odottamaan toisiaan: toinen haluaa toisen jo varaamaa lukkoa tämä tilanne on ns. lukkiuma (deadlock)

Deadlock-ongelman ratkaisuja (osin SICP 3.4.2) kaksi lukkiumaongelman ratkaisua: varataan lukot aina samassa järjestyksessä (estää lukkiuman syntymisen, mutta hankala toteuttaa, ellei tiedä etukäteen, mitä lukkoja tarvitsee) tunnistetaan lukkiuma ja puretaan se pyytämällä yhtä osallisista peruuttamaan miten lukkiuma tunnistetaan?

Deadlock-ongelman ratkaisuja (osin SICP 3.4.2) kaksi lukkiumaongelman ratkaisua: varataan lukot aina samassa järjestyksessä (estää lukkiuman syntymisen, mutta hankala toteuttaa, ellei tiedä etukäteen, mitä lukkoja tarvitsee) tunnistetaan lukkiuma ja puretaan se pyytämällä yhtä osallisista peruuttamaan miten lukkiuma tunnistetaan? esim. rakennetaan verkko siitä, mikä säie odottaa minkä säikeen varaamaa lukkoa tämän ns. odotusverkon sykli on lukkiuma vastaava ongelma on livelock: muuten sama, mutta ohjelmat eivät odota vaan tekevät jotain aktiivisesti silmukassa kunnes toinen valmistuu (eli ikuisesti) nämä ongelmat näkyvät jossain muodossa jokseenkin kaikissa rinnakkaisuuden muodoissa (aina kun varataan usempi jaettu resurssi)

Mitä ongelmille voi tehdä? käytännössä rinnakkaisuutta käytettäessä esimerkiksi: määritellään tarkkaan yhteisten resurssien käyttötapa (esim. mitä lukkoja pitää varata ennen kuin tiettyä muuttujaa käsitellään) pidetään rinnakkaisten ohjelman osien keskinäinen kommunikointi tavallistakin selkeämpänä (ja usein mahdollisimman vähäisenä) määritellään järjestys, jossa lukot tms. pitää varata joskus todistetaan rinnakkaisuuteen liittyviä ominaisuuksia käytetään rinnakkaisuutta paljon vähemmän kuin olisi periaatteessa mahdollista periaatteessa ongelmat liittyvät aina eri säikeiden väliseen kommunikointiin (yhteisten muuttujien käyttäminen on eräänlaista kommunikointia): esimerkiksi muille säikeille näkymättömistä välituloksista ei tarvitse huolehtia read-only-resurssit ovat helpompia: jos arvo ei muutu, sen rinnakkaisesta lukemisesta ei tarvitse huolehtia

Sisältö 1 Säikeet 2 Lukot 3 Monitorit 4 Muuta säikeisiin liittyvää 5 Rinnakkaisuuden ja säiemallin ongelmia 6 Muita rinnakkaisuusmalleja

Muita tapoja tehdä rinnakkaisuutta edellä kuvattu säiemalli on yleisin tapa tehdä rinnakkaisuutta (toistaiseksi?) toinen yleinen malli on ajaa erillisiä ohjelmia (prosesseja), jotka kommunikoivat esimerkiksi verkkoyhteyksillä tai tiedostoilla säiemallin vaihtoehdoksi on tehty monia muitakin rinnakkaisuusmalleja, esimerkiksi: yksittäisiä abstraktimpia operaatioita esim. taustalaskentaan transaktionaalinen muisti viestinvälitykseen perustuvia menetelmiä: säikeillä ei ole (tai ei näytä olevan) jaettua muistia, vaan ne kommunikoivat lähettämällä toisilleen viestejä lisäksi on hajautettu laskenta (distributed computing): rinnakkaisuutta niin, että eri säikeet pyörivät eri koneissa (yleensä perustuen viestinvälitykseen)

Abstraktimpia rinnakkaisuusprimitiivejä (SbE 17.317.4) Scalassa on val x = future( arvon laskeva lauseke ) siirtää arvon laskemisen taustalle toiseen säikeeseen jos arvoa yritetään käyttää ennen kuin taustalaskenta on valmis, säie jää odottamaan sitä par( lauseke1, lauseke2 ) laskee kaksi lauseketta rinnakkain, odottaa niitä ja palauttaa parin, jossa on molempien arvot parmap on versio mapista, joka laskee (esim. taulukon) alkiot rinnakkain eri säikeissä näitä on enemmänkin, ja samantapaisia voi määritellä itse Scalassa nämä on toteutettu ns. synkronoidun muuttujan avulla: se on pieni monitorilla tehty luokka, johon voi sijoittaa arvon ja jonka get-metodi jää odottamaan, että joku sijoittaa sinne jonkin arvon näiden etu on, että lukituksia ei tarvitse miettiä itse (paitsi esim. lukkiuman mahdollisuutta)

Transaktionaalinen muisti: toiminta transaktionaalinen muisti eli (software) transactional memory perustuu siihen, että rinnakkainen koodi jaetaan transaktioihin lukituksen sijaan transaktiota suoritetaan optimistisesti: luetaan arvoja ilman lukkoja ja tarkistetaan lopuksi että kaikki meni oikein keskeneräinen transaktio ollaan aina valmis perumaan (perutaan sen muistiin tekemät muutokset) muiden säikeiden kannalta transaktio on atominen: joko koko transaktion tekemä muutos näkyy muille säikeille, tai mitään osaa siitä ei näytä vielä olevan tehty ohjelmoijan pitää jakaa rinnakkainen koodi transaktioihin (niistä kohdista, joissa jokin muistiin tehty muutos on valmis näytettäväksi muille säikeille) ja säikeet pitää (yleensä) käynnistää itse mutta lukituskoodia tms. rinnakkaisuudenhallintaa ei tarvitse tehdä (vain transaktiorajat) myös transaktion peruutus tehdään automaattisesti

Transaktionaalinen muisti: toteuttaminen transaktionaalisesta muistista on kaksi perustoteutusta: software transactional memory: transaktiot toteutetaan ohjelmallisesti lukoilla ja/tai versioiduilla muistipaikoilla hardware transactional memory: toteutetaan osittain prosessorien sisällä (usein näissä transaktiot ovat rajoittuneempia) ohjelmallinen toteutus lienee yleisempi transaktionaalisessa muistissa on kuitenkin käytännön rajoituksia esim. transaktiot eivät käytännössä voi olla kovin isoja sisäkkäiset transaktiot ovat joskus ongelmallisia usein joitain operaatioita ei voi tehdä transaktion sisällä (esim. I/O vähintään puskuroidaan tehtäväksi transaktion lopuksi) transaktionaalinen muisti on melko uusi käsite eikä ole vielä kovin yleisesti käytössä (ehkä tulossa?) lisätietoja: ks. esim. Wikipediasta Software transactional memory

Scalan actor-malli (SbE 3) Scalan actor-malli perustuu viestinvälitykseen: ohjelma jaetaan pieniin osiin, jotka vain vastaanottavat ja käsittelevät viestejä toisiltaan viesti voi olla esim. laskentapyyntö tai haku tietorakenteesta, ja yleensä paluuviestissä lähetetään vastaus vrt. että olion metodikutsu vastaa viestin lähettämistä sille (mutta Actor-mallissa viestit ovat usein hieman isompia kokonaisuuksia) Scala jakaa actorit automaattisesti eri säikeisiin malli taitaa olla lainattu Scalaan Erlang-kielestä (dynaamisesti tyypitetty funktionaalinen kieli), mutta alun perin se on peräisin jo 1970-luvulta koodiesimerkki: katso SbE luku 3 lisätietoja esim. V. Subramanian: Programming Scala luku 10