Muutettu viimeksi 26.4.2010 / 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) } } }Aktori käynnistetään start-metodilla:
object Silly extends Application { SillyActor.start SeriousActor.start } // Tulostaa hidastellen: // I'm acting! // To be or not to be. // 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-metodilla:
import scala.actors.Actor._ object Hamlet extends Application { 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 Application { 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 merkkijonoTehdas = 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 Application { 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 merkkijonoTehdas = 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 ! Tuple("hau", 42, 'miau) } } }Esimerkkitulostus (huom: ohjelman saa päättymään ctrl-c:llä):
vastaanotettu kokonaisluku: 1 vastaanotettu merkkijono: kissa numero 1 vastaanotettu jotakin: List(3, 1) vastaanotettu jotakin: (hau,42,'miau) vastaanotettu jotakin: List(3, 1) vastaanotettu kokonaisluku: 2 vastaanotettu merkkijono: kissa numero 2 vastaanotettu jotakin: (hau,42,'miau) vastaanotettu jotakin: List(3, 1) vastaanotettu kokonaisluku: 3 vastaanotettu jotakin: (hau,42,'miau) vastaanotettu merkkijono: kissa numero 3 vastaanotettu kokonaisluku: 4 vastaanotettu merkkijono: kissa numero 4
import scala.actors.Actor._ object Tuotantoketju extends Application { val alkutuotanto = actor { while (true) { jalostus ! Math.random Thread.sleep(500) } } val jalostus = actor { while (true) { receive { case msg: Double => koristelu ! (msg * 1000 toInt) } } } val koristelu = actor { while (true) { receive { case msg => myynti ! "** " + msg + " **" } } } val myynti = actor { var lkm = 0 while (true) { receive { case msg => ostaja ! msg lkm += 1 } println("Myyty " + lkm + "kpl") } } val ostaja = actor { while (true) { receive { case msg => println("Ostin juuri uuden hienon " + msg) } } } }Esimerkkitulostusta (huom: ohjelman saa päättymään ctrl-c:llä):
Myyty 1kpl Ostin juuri uuden hienon ** 858 ** Myyty 2kpl Ostin juuri uuden hienon ** 206 ** Myyty 3kpl Ostin juuri uuden hienon ** 524 ** Myyty 4kpl Ostin juuri uuden hienon ** 899 ** Myyty 5kpl Ostin juuri uuden hienon ** 948 ** Myyty 6kpl Ostin juuri uuden hienon ** 895 ** Myyty 7kpl Ostin juuri uuden hienon ** 53 ** ...