815338A Ohjelmointikielten periaatteet 2015-2016 IX Rinnakkainen ohjelmointi
Sisältö 1. Yleistä rinnakkaisuudesta 2. Prosesseista ja säikeistä 3. Rinnakkaisen ohjelman oikeellisuudesta 4. Rinnakkaisuuden kontrollirakenteet 5. Rinnakkaisuus Javassa 6. Rinnakkaisuus C++-kielessä 7. Rinnakkaisuus muissa ohjelmointikielissä 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 2
IX.1. Yleistä rinnakkaisuudesta Peräkkäinen vs. Rinnakkainen Käskyt suoritetaan peräkkäin Yksikäsitteinen suorituspolku Deterministinen sama syöte, sama tulos aina Toimintoja suoritetaan rinnakkain Ei selvää suorituspolkua Epädeterministinen tulos voi riippua suoritusjärjestyksestä 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 3
IX.1.1. Rinnakkaisuuden tyypit Parallel programming Aito (fyysinen) rinnakkaisuus Vaatii useampia prosessoreja (tai ainakin ytimiä) Concurrent programming Rinnakkaisuus voi olla harhaa: suoritetaan yksi konekielinen käsky kerrallaan Toiminnot rinnakkain looginen rinnakkaisuus Toimii yhdessä prosessorissa 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 4
IX.2. Prosesseista ja säikeistä IX.2.1. Prosessi *x++=*y++ Tapa ajaa useita ohjelmia rinnakkain yhdessä prosessorissa Oma muistialue Kommunikointi esim. putkilla Rinnakkainen suorittaminen aikajaolla Ominaisuudet riippuvat käyttöjärjestelmästä 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 5
IX.2.2. Säikeet Prosessia kevyempiä Prosessi voidaan jakaa säikeisiin Säikeellä oma ohjelmalaskuri ja pino Säikeet jakavat prosessin muistialueen ja resurssit Käyttöjärjestelmissä standardi: Pthread (POSIX: IEEE 1003.1) Tässä monisäieohjelmointia Liittyy läheisemmin ohjelmointikieliin 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 6
IX.2.2.1 Säikeen elinkaari *x++=*y++ Uusi Käynnistys Ajettava/ Suorituksessa suoritus loppuu Kuollut odotus sleep() IO esto Estetty ilmoitus sleep aika IO suoritus suoritus loppuu 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 7
IX.3. Rinnakkaisen ohjelman oikeellisuudesta Rinnakkaisen ohjelman oikeellisuuskriteerit: Turvallisuus (safety) Oliot ja muuttujat pysyvät kunnossa Eloisuus (liveness) Kaikki aiotut operaatiot suoritetaan joskus *x++=*y++ 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 8
IX.3.1. Turvallisuus Ei-atomaarinen operaatio voi epäonnistua jos kaksi säiettä toimii yhtä aikaa Metodi voi toimia väärin jos sitä kutsutaan kahdesta säikeestä Säieturvallinen (thread safe): toimii millä tahansa kutsujärjestyksellä Muuttujan lukeminen ja arvon kirjoittaminen samanaikaisesti = Luku/Kirjoituskonflikti Muuttujan arvon kirjoittaminen samanaikaisesti kahdesta säikeestä= Kirjoitus/Kirjoituskonflikti Yleistermi kilpailutilanne (race condition) 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 9
IX.3.2. Eloisuus Aiotut operaatiot suoritetaan ei aikarajaa Reaaliaikainen ohjelmointi asetetaan aikarajoja Turvallisuus ja eloisuus jossain määrin vastakkaisia 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 10
IX.3.3. Operaation epäonnistuminen Operaation suorittamatta jäämisen yleisin syy: odotetaan resurssin vapautumista, esimerkiksi Toinen säie on lukinnut suoritettavan metodin Metodi lukkiutuu odottaen toisen säikeen suorittamaa tapahtumaa Pysyvän epäonnitumisen syitä: Lukkiutuminen (deadlock) Säikeet ristiinlukitsevat metodit Nälkiintyminen (starvation) Säie ei saa lainkaan keskusyksiköltä ajoaikaa Voi johtua monista syistä 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 11
IX.3.4. Synkronointi Säikeiden jaksottaminen niin, että ohjelman oikeellisuus varmistuu Kilpailun synkronointi (competition synchronization) Tarvitaan, kun kaksi säiettä yrittää toisistaan riippumatta käyttää jotakin resurssia Toteutus yleensä: Säie pyytää synkronointioliolta resurssin käyttöoikeutta, vapauttaa resurssin lopetettuaan operaationsa 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 12
IX.3.4. Synkronointi (2) Yhteistoiminnan synkronointi (cooperation synchronization). Tarvitaan, kun jonkin säikeen A operaation suorittaminen riippuu toisen säikeen B toiminnasta -> A joutuu odottamaan, kunnes B saa suoritettua operaationsa 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 13
IX.4. Rinnakkaisuuden kontrollirakenteet Synkronoinnin hallinnan perusmekanismit Semaforit, Monitorit, Viestinvälittäminen Viestinvälittäminen (message passing) Välttämätön, jollei yhteistä muistialuetta Adan perusmekanismi Jaksottaminen hoidetaan lähettämällä ja vastaanottamalla viestejä 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 14
IX.4.1. Semaforit Vanhimpia rinnakkaisen ohjelmoinnin rakenteita 1968 Dijkstra: THE käyttöjärjestelmä E.W. Dijkstra, The structure of THE multiprogramming system, Communications of ACM, 11 (5), 1968, 341-346. Semafori-nimitys rautateiden opastimista Semafori: Kokonaislukumuuttuja, joka pitää yllä lupien lukumäärää, arvo 0 Lupien lukumäärä kuvaa yleensä yhtä aikaa toimivia säikeitä 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 15
IX.4.1. Semaforit (2) Säie pääsee toimimaan jos saa luvan semaforilta, muuten odottaa Kun säie lopettaa toimintansa on ilmoitettava semaforille Operaatiot ( S on semafori) WAIT(S) (tai P(S)) SIGNAL(S) (tai V(S)) 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 16
IX.4.1. Semaforit (3) Voidaan käyttää esim. Poissulkevuuden toteuttamiseen Laskurina Periaatteessa kaikki rinnakkaisuuden rakenteet voitaisiin toteuttaa pelkästään semaforeilla Monissa tapauksissa kömpelöä Monissa käyttöjärjestelmissä semafori on ainoa rinnakkaisuuden kontrollirakenne UNIX ja Windows tukevat suoraan semaforeja Melko harvinaisia ohjelmointikielissä Algol68 ja PL/I 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 17
IX.4.2. Monitorit Semaforit eivät sovi hyvin rakenteiseen ohjelmointiin -> Tarvittiin uusi rakenne, joka käyttäisi datan kapselointia C.A.R. Hoare 1974 Esitteli monitorin käsitteen Monitors: an operating system structuring concept, Communications of ACM, 17 (10) 549-557, 1974 Concurrent Pascal (Brinch Hansen 1975) Ensimmäinen ohjelmointikieli, jossa monitorit käytössä 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 18
IX.4.2. Monitorit (2) Kapseloivat halutut toiminnot sisäänsä Suoritus mahdollinen vain monitorin suostumuksella Vain yksi säie kerrallaan: säikeellä hallussaan monitorin lukko (lock) Monitorin odotusjoukko (wait set) Sisältää suoritusta odottavat säikeet Voi jakaantua osajoukkoihin ehtomuuttujien (condition variables) suhteen 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 19
IX.4.2. Monitorit (3) Ilmoittaminen (notify, signal) Lukkoa hallussaan pitävän säikeen luopuminen lukosta Odotusjoukosta valitaan jokin säie toimimaan Mahdollista osoittaa, että monitori voidaan toteuttaa semaforeilla ja päinvastoin -> monitori ja semafori ilmaisuvoimaltaan yhtä vahvoja Monitori kuitenkin käytettävyydeltään korkeammalla tasolla 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 20
IX.5. Rinnakkaisuus Javassa IX.5.1. Javan säikeet *x++=*y++ Käyttäjäsäikeet (user threads) varsinaiset säikeet Demonisäikeet (daemon threads) toimivat taustalla Ohjelma loppuu, kun sen kaikki käyttäjäsäikeet päättyvät tai kutsutaan Runtime-luokan exit-metodia (koodissa System.exit(0);) 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 21
IX.5.2.Säikeen luominen Javassa Kirjoita luokka, joka perii Thread-luokan run-metodi ylikirjoitettava TAI Kirjoita luokka joka toteuttaa Runnable-rajapinnan toteutettava run-metodi 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 22
IX.5.2.Säikeen luominen Javassa Thread-luokan oliot kontrolloivat säikeitä: 1. Luo uusi Thread-olio 2. Konfiguroi Anna prioriteetti ja nimi (ei pakollinen) 3. Käynnistä kutsumalla start-metodia HUOM1: Käynnistys kutsumalla startia, toiminnallisuus runissa! HUOM2: Säie päättyy, kun run loppuu. 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 23
IX.5.3. Synkronointi Javassa Javassa monitori sisäänrakennettu rinnakkaisuuden hallintamekanismi Jokainen olio voi toimia monitorina -> jokaisella oliolla on lukko ja odotusjoukko Javan monitorit Ilmoita ja jatka -tyyppiä Ilmoituksen saanut säie odottaa, kunnes ilmoittaja poistuu monitorista ja alkaa tämän jälkeen suorittaa toimintojaan 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 24
IX.5.3.1. Esimerkki Javan synkronoinnista: syklinen puskuri Käytetään, kun yksi säie tuottaa toiselle säikeelle dataa ja data on saatava talteen, kunnes se on luettu. Voidaan toteuttaa taulukkona Kirjoitus ja lukeminen nopeaa ja yksinkertaista Pidettävä huoli siitä, että täyteen puskuriin ei enää kirjoiteta eikä tyhjästä lueta Viimeisen paikan jälkeen kirjoitetaan aina ensimmäiseen Luokkaan kirjoitetaan metodit put ja take, joilla kirjoitetaan puskuriin ja luetaan siitä 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 25
IX.5.3.1.1. Kilpailun synkronointi syklisessä puskurissa Jos metodeja put ja take kutsutaan kahdesta säikeestä yhtä aikaa, voi tulla konflikti -> kilpailun synkronoinnilla estettävä Onnistuu kirjoittamalla metodit syknronoiduiksi = lisätään sen esittelyyn sana synchronized -> olio itse toimii monitorina 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 26
IX.5.3.1.1. Kilpailun synkronointi syklisessä puskurissa (2) Säie kutsuu synkronoitua metodia -> ottaa haltuunsa olion lukon Olion kaikkien synkronoitujen metodien suorittaminen estyy kunnes säie luopuu lukosta Lukosta luopuminen: 1.Synkronoitu metodi loppuu tai 2.Säie siirtyy odotustilaan 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 27
IX.5.3.1.2. Yhteistoiminnan synkronointi syklisessä puskurissa Yo. toteutuksen jälkeen koodissa vielä ongelma: tyhjästä puskurista luetaan ja täyteen kirjoitetaan. Oikea toiminta: Lukeva säie pysähtyy, ellei mitään luettavaa ole. Jatkaa toimintaansa vasta sitten, kun jokin kirjoittajasäie käy kirjoittamassa alkion taulukkoon. Kirjoittava säie pysähtyy, jos puskuri täynnä. Saa jatkaa toimintaansa vasta, kun jokin lukijasäie käy lukemassa alkion 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 28
IX.5.3.1.2. Yhteistoiminnan synkronointi syklisessä puskurissa (2) Käytetään wait/notify-metodeja wait, notify ja notifyall ovat Object-luokan metodeja Koska kaikki oliot perivät Object-luokan, kaikilla olioilla on myös nämä metodit wait luopuu lukosta ja jää odottamaan ilmoitusta notify ja notifyall annettuna toisesta säikeestä vapauttavat odottavia säikeitä Yhdistetään sekä kilpailun että yhteistoiminnan synkronointi -> Ratkaisu sykliselle puskurille 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 29
IX.5.3.1.3. Luokan koodi public class CircularBuffer{ protected final Object[] buffer; int putptr = 0;int takeptr = 0; int queueditems = 0; public CircularBuffer(int size){ buffer = new Object[size]; } // Put objects to buffer public synchronized void put(object obj) throws InterruptedException{ while(queueditems ==buffer.length) wait(); buffer[putptr] = obj; putptr = (putptr+1)%buffer.length; queueditems++; *x++=*y++ // Take objects from buffer public synchronized Object take() throws InterruptedException{ while(queueditems == 0) wait(); Object retobj = buffer[takeptr]; takeptr = (takeptr+1) %buffer.length; queueditems--; if(queueditems == (buffer.length-1)) notifyall(); if(queueditems == 1) notifyall(); } // End put() return retobj; } // End take() } // End class CircularBuffer 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 30
IX.6. Rinnakkaisuus C++-kielessä IX.6.1 Säikeet Standardiin C++11 lisätty rinnakkaisuuden tuki Säikeiden hallintaan luokka std::thread *x++=*y++ Voi suorittaa minkä tahansa funktion koodia omassa säikeessään Toinen säie voi odottaa päättymistä (metodi join) Ohjelma päättyy, kun pääohjelman säie loppuu -> voidaan tarvita join-kutsua Voidaan irrottaa taustasäikeeksi (metodi detach) Ei voida odottaa loppumista 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 31
IX.6.1 Säikeet. Esimerkki #include <iostream> #include <thread> void terve(){ std::cout << "Terve. Olen saie " << std::this_thread::get_id() << std::endl; } int main(){ std::thread saie(terve); saie.join(); std::cout << "Ohjelman loppu!" << std::endl; return 0; } 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 32
IX.6.2 Kilpailun synkronointi C++-kielessä Koodin lukitseminen muilta säikeiltä: Kutsutaan luokan std::mutex olion lock-metodia Ei voida suorittaa muista säikeistä ennen unlockkutsua Esimerkki: std::mutex lukko; void kasvata(){ } lukko.lock(); ++arvo; lukko.unlock(); Lukua arvo voi kasvattaa vain yhdestä säikeestä kerrallaan 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 33
IX.6.3 Yhteistoiminnan synkronointi C++-kielessä C++:ssa ei monitoria vastaavaa luokkaa Odottaminen ja ilmoittaminen ehtomuuttujilla: luokka std::condition_variable Esimerkki std::mutex lukko; std::condition_variable conone; std::condition_variable contwo; void kasvata(){ } std::unique_lock<std::mutex> lck(lukko); while(arvo >= max) conone.wait(lck); ++arvo; contwo.notify_one(); 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 34
IX.7. Rinnakkaisuus muissa ohjelmointikielissä PL/I IBM, 1960 luvun puoliväli *x++=*y++ Ensimmäinen rinnakkaista ohjelmointia tukeva kieli Rinnakkaisuuden toteutus oli puutteellinen Simula (1966 67) Ensimmäinen oliokieli (joidenkin lähteiden mukaan ensimmäinen rinnakkainen kieli) Tuki myös rinnakkaisuutta (alkeelliseesti vuorottelualiohjelmien [coroutines] avulla) 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 35
IX.7. Rinnakkaisuus muissa ohjelmointikielissä (2) Pascal C Ei tue rinnakkaisuutta 1970 luvun puolivälissä Concurrent Pascal Sisälsi ensimmäisenä monitorin Ei tue rinnakkaisuutta Kehitetty Concurrent C Voidaan toteuttaa rinnakkaisuus käyttöjärjestelmän ominaisuuksien avulla 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 36
IX.7. Rinnakkaisuus muissa ohjelmointikielissä (3) Ada C# 1983 Yhdysvaltain puolustusministeriön kehitysprojekti Tukee rinnakkaista ohjelmointia (Adan säie = task) Synkronointi perustuu viestinvälitykseen 1995 parannettu versio Rinnakkaisuus samankaltainen kuin Javassa Muutokset Sebestan mukaan merkittäviä ja parantavat Javan säietoteutusta 815338A Ohjelmointikielten periaatteet, Rinnakkainen ohjelmointi 37