Muutettu viimeksi 15.2.2012 / Sivu luotu 23.4.2010 / [oppikirjan esimerkit] / [Scala]
Sivun sisältöä:
Tässä luvussa nähdään vain Scalan aktoreiden perusidea. Rinnakkaisuuden syvällisempi käsittely säästetään muille kursseille.
Yksi rinnakkaisohjelmoinnin monista ongelmista on jaetun muistin suojaus. Aktorimallissa jaettu muisti ei tuota ongelmia, koska aktorit eivät jaa muistia! Aktorit sen sijaan lähettelevät toisilleen viestejä ja vastaanottavat muilta aktoreilta tulleita viestejä. Viestien vastaanottoon aktorilla on postilaatikko.
Aktoreita voi luoda scala.actors.Actor-luokan aliluokkina (tai tässä "aliolioina") ohjelmoimalla act()-metodin:
import scala.actors._ object SillyActor extends Actor { def act() { for (i <- 1 to 5) { println("I'm acting!") Thread.sleep(700) } } } object SeriousActor extends Actor { def act() { for (i <- 1 to 5) { println("To be or not to be.") Thread.sleep(1000) } } } SillyActor.start SeriousActor.startAktori käynnistetään start-metodilla. Ohjelma tulostaa hidastellen esim.:
To be or not to be. I'm acting! I'm acting! To be or not to be. I'm acting! To be or not to be. I'm acting! I'm acting! To be or not to be. To be or not to be.Aktorin voi valmistaa myös metodilla scala.actors.Actor.actor:
import scala.actors.Actor._ // (huom: importoidaan luokan metodit!) val hamlet = actor { for (i <- 1 to 5) { println("That is the question.") Thread.sleep(1000) } }Näin luotu aktori käynnistyy saman tien ilman mitään start-pyyntöä.
aktori ! viestiKun aktori lähettää viestin, se ei pysähdy odottamaan mitään. Kun aktori saa viestin, sen toiminta ei keskeydy, mutta halutessaan aktori voi mennä lukemaan postilaatikkoaan lauseella receive:
receive { case jotakin: Tyyppi => toimintaa case jotakinmuuta: ToinenTyyppi => muuta toimintaa ... case eityyppiä => toimintaa muiden kuin nimettyjen tyyppien tapauksessa }Jos postilaatikosta ei löydy mitään vastaanotettavan tyyppistä viestiä, aktori odottaa, kunnes sellainen tulee. Jos viimeisenä on tyypitön case-vaihtoehto, minkä tahansa tyyppinen viesti vastaanotetaan.
Esimerkki.
import scala.actors.Actor._ object Kaiku extends App { val kaiuttaja = actor { while (true) { receive { // tälle mikä vain kelpaa case msg => println("vastaanotettu viesti: "+ msg) } } } val lukuTehdas = actor { for (i <- 1 to 10) { kaiuttaja ! i Thread.sleep(700) } } val kissaTehdas = actor { for (i <- 1 to 10) { kaiuttaja ! "kissa numero " + i Thread.sleep(1000) } } }Esimerkkitulostus (huom: ohjelman saa päättymään ctrl-c:llä):
vastaanotettu viesti: kissa numero 1 vastaanotettu viesti: 1 vastaanotettu viesti: 2 vastaanotettu viesti: kissa numero 2 vastaanotettu viesti: 3 vastaanotettu viesti: kissa numero 3 vastaanotettu viesti: 4 vastaanotettu viesti: 5 vastaanotettu viesti: kissa numero 4 vastaanotettu viesti: 6 vastaanotettu viesti: kissa numero 5 vastaanotettu viesti: 7 vastaanotettu viesti: 8 vastaanotettu viesti: kissa numero 6 vastaanotettu viesti: 9 vastaanotettu viesti: kissa numero 7 vastaanotettu viesti: 10 vastaanotettu viesti: kissa numero 8 vastaanotettu viesti: kissa numero 9 vastaanotettu viesti: kissa numero 10
Esimerkki, jossa eri tyyppisiä viestejä käsitellään eri tavoin:
import scala.actors.Actor._ object EriKaiku extends App { val kaiuttaja = actor { while (true) { receive { case msg: String => println("vastaanotettu merkkijono: "+ msg) case msg: Int => println("vastaanotettu kokonaisluku: "+ msg) case msg => println("vastaanotettu jotakin: "+ msg) } } } val lukuTehdas = actor { for (i <- 1 to 4) { kaiuttaja ! i Thread.sleep(700) } } val kissaTehdas = actor { for (i <- 1 to 4) { kaiuttaja ! "kissa numero " + i Thread.sleep(1000) } } val jokinTehdas = actor { for (i <- 1 to 3) { kaiuttaja ! List(3,1) Thread.sleep(500) kaiuttaja ! Map("koira" -> "hau", "kissa" -> "miau") } } }Esimerkkitulostus (huom: ohjelman saa päättymään ctrl-c:llä):
vastaanotettu merkkijono: kissa numero 1 vastaanotettu kokonaisluku: 1 vastaanotettu jotakin: List(3, 1) vastaanotettu jotakin: Map(koira -> hau, kissa -> miau) vastaanotettu jotakin: List(3, 1) vastaanotettu kokonaisluku: 2 vastaanotettu merkkijono: kissa numero 2 vastaanotettu jotakin: Map(koira -> hau, kissa -> miau) vastaanotettu jotakin: List(3, 1) vastaanotettu kokonaisluku: 3 vastaanotettu jotakin: Map(koira -> hau, kissa -> miau) vastaanotettu merkkijono: kissa numero 3 vastaanotettu kokonaisluku: 4 vastaanotettu merkkijono: kissa numero 4
import scala.actors.Actor._ object Tuotantoketju extends App { val alkutuotanto = actor { while (true) { Thread.sleep(500) // Töitä tehdään... println("Koneet käy: runks, runks, runks, ...") jalostus ! math.random } } val jalostus = actor { while (true) { receive { case msg: Double => println("Jalostetaan " + msg) koristelu ! (msg * 1000 toInt) } } } val koristelu = actor { while (true) { receive { case msg => println("Koristellaan " + msg) myynti ! "** " + msg + " **" } } } val myynti = actor { var lkm = 0 while (true) { receive { case msg => lkm += 1 println("Myyty tuote " + msg + ". Kokonaismyynti: " + lkm + " kpl.") } } } }Esimerkkitulostusta (huom: ohjelman saa päättymään ctrl-c:llä):
Koneet käy: runks, runks, runks, ... Jalostetaan 0.09351960144560689 Koristellaan 93 Myyty tuote ** 93 **. Kokonaismyynti: 1 kpl. Koneet käy: runks, runks, runks, ... Jalostetaan 0.8703921180993694 Koristellaan 870 Myyty tuote ** 870 **. Kokonaismyynti: 2 kpl. Koneet käy: runks, runks, runks, ... Jalostetaan 0.6863182839097028 Koristellaan 686 Myyty tuote ** 686 **. Kokonaismyynti: 3 kpl. Koneet käy: runks, runks, runks, ... Jalostetaan 0.9782239841378854 Koristellaan 978 Myyty tuote ** 978 **. Kokonaismyynti: 4 kpl. Koneet käy: runks, runks, runks, ... Jalostetaan 0.7204195334294463 Koristellaan 720 Myyty tuote ** 720 **. Kokonaismyynti: 5 kpl. Koneet käy: runks, runks, runks, ... Jalostetaan 0.9264337052100224 Koristellaan 926 Myyty tuote ** 926 **. Kokonaismyynti: 6 kpl. Koneet käy: runks, runks, runks, ... Jalostetaan 0.6971132918108124 Koristellaan 697 Myyty tuote ** 697 **. Kokonaismyynti: 7 kpl. Koneet käy: runks, runks, runks, ... ...