Kotlin Academy · Oppitunti

Actorit ja rakenteinen tilanhallinta

Käyttäkää actor-tyylisiä malleja tilan käytön sarjallistamiseen samanaikaisessa koodissa.

Oppitunti 4/413 vaihetta

Actorit ja rakenteinen tilanhallinta on ilmainen Kotlin Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Kotlin Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Mikä on aktori

Aktori on korutiini, joka omistaa yksityisen muokattavan tilan ja viestii kanavan kautta. Ulkoinen koodi ei koskaan käytä tilaa suoraan, vaan ainoastaan viestien kautta.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
sealed class CounterMsg
object Increment : CounterMsg()
class GetCount(val response: CompletableDeferred<Int>) : CounterMsg()
fun CoroutineScope.counterActor() = actor<CounterMsg> {
    var counter = 0
    for (msg in channel) {
        when (msg) {
            is Increment -> counter++
            is GetCount  -> msg.response.complete(counter)
        }
    }
}

Aktorin käyttäminen

Lähettäkää viestejä aktorille käyttämällä send-metodia. Aktori käsittelee viestit yksi kerrallaan ja sarjallistaa tilan käytön.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
// (CounterMsg sealed class from previous scene)
fun main() = runBlocking {
    val counter = counterActor()
    repeat(100) { counter.send(Increment) }
    val response = CompletableDeferred<Int>()
    counter.send(GetCount(response))
    println("Count: ${response.await()}") // 100
    counter.close()
}

Aktori korvaa Mutexin

Aktori poistaa mutexien tarpeen tekemällä tilan käytöstä rakenteellisesti peräkkäistä. Kaikki muutokset tehdään yhden korutiinin sisällä.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
sealed class Msg
object Inc : Msg()
data class Get(val d: CompletableDeferred<Int>) : Msg()
fun CoroutineScope.safeCounter() = actor<Msg> {
    var n = 0
    for (m in channel) when(m) {
        is Inc -> n++
        is Get -> m.d.complete(n)
    }
}
fun main() = runBlocking {
    val a = safeCounter()
    repeat(1000) { a.send(Inc) }
    val d = CompletableDeferred<Int>()
    a.send(Get(d))
    println(d.await()) // 1000
    a.close()
}

Tyypitetty viestiprotokolla

Määritelkää viestiprotokolla sealed class -luokkahierarkiana, jotta kaikki tapaukset voidaan käsitellä aktorin when-lausekkeessa.

sealed class BankMsg
data class Deposit(val amount: Double) : BankMsg()
data class Withdraw(val amount: Double, val result: CompletableDeferred<Boolean>) : BankMsg()
data class Balance(val result: CompletableDeferred<Double>) : BankMsg()
// Actor holds balance privately — outside code sends messages only

Tilakone aktorina

Aktori toimii luontevasti tilakoneena: sisäiset tilasiirtymät tapahtuvat atomisesti viestien seurauksena.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
enum class TrafficState { RED, GREEN, YELLOW }
fun CoroutineScope.trafficLight() = actor<Unit> {
    var state = TrafficState.RED
    for (msg in channel) {
        state = when (state) {
            TrafficState.RED    -> TrafficState.GREEN
            TrafficState.GREEN  -> TrafficState.YELLOW
            TrafficState.YELLOW -> TrafficState.RED
        }
        println("State: $state")
    }
}
fun main() = runBlocking {
    val light = trafficLight()
    repeat(6) { light.send(Unit) }
    light.close()
}

actor()-rakentaja

actor { }-korutiinin rakentaja luo actorin, jonka viestipostilaatikkona toimii kanava. Actor on korutiini, joka käsittelee channel-kanavasta tulevia viestejä.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun CoroutineScope.logActor() = actor<String>(capacity = Channel.BUFFERED) {
    val log = mutableListOf<String>()
    for (msg in channel) {
        log.add(msg)
        println("[LOG] $msg")
    }
    println("Log entries: ${log.size}")
}
fun main() = runBlocking {
    val logger = logActor()
    repeat(5) { logger.send("Event $it") }
    logger.close()
    // actor finishes after close
}

Actorin pysäyttäminen

Sulje actorin lähetyskanava kutsumalla close()-metodia. Actorin for-silmukka päättyy, jolloin siivoustoimet voidaan suorittaa ennen korutiinin päättymistä.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val actor = actor<String> {
        for (msg in channel) println("Got: $msg")
        println("Actor done")
    }
    actor.send("hello")
    actor.send("world")
    actor.close()
    // Wait for actor to finish
    delay(50)
}

Actorit ja backpressure

Määritä actorin kanavan kapasiteetti, jotta backpressure tulee käyttöön — lähettäjät keskeyttävät suorituksensa, kun postilaatikko on täynnä.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun CoroutineScope.slowProcessor() = actor<Int>(capacity = 2) {
    for (item in channel) {
        delay(100) // slow processing
        println("Processed: $item")
    }
}
fun main() = runBlocking {
    val proc = slowProcessor()
    repeat(5) { proc.send(it) } // sender suspends when capacity full
    proc.close()
}

Actorin ja Mutexin suorituskyky

Actorit sarjoittavat käytön ilman lukituskilpailua ja sopivat monimutkaiseen tilaan, jossa on useita viestityyppejä. Mutex on yksinkertaisempi yhden jaetun laskurin tapauksessa, mutta sitä on vaikeampi yhdistellä.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
// For a simple counter: Mutex is fine
val mutex = Mutex()
var simpleCounter = 0
// For complex state + multiple operations: Actor is cleaner
// Actor: messages describe intent; state changes are encapsulated
fun main() = runBlocking { println("Choose based on state complexity") }

CompletableDeferred pyyntö–vastausmallissa

Kun actorin sisällä käytetään pyyntö–vastausmallia, sisällytä viestiin CompletableDeferred. Actor täydentää sen, ja lähettäjä odottaa sen valmistumista.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
data class Query(val key: String, val reply: CompletableDeferred<String?>)
fun CoroutineScope.cacheActor() = actor<Any> {
    val cache = mutableMapOf<String, String>()
    for (msg in channel) when (msg) {
        is Pair<*, *> -> cache[msg.first as String] = msg.second as String
        is Query -> msg.reply.complete(cache[msg.key])
    }
}

Moderni vaihtoehto: StateFlow + coroutineScope

actor-rakentaja on kokeellinen, ja se saatetaan poistaa käytöstä. Moderni vaihtoehto on ViewModel- tai palveluluokka, joka päivittää MutableStateFlow-oliota yhdestä korutiinista.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
class CounterService(scope: CoroutineScope) {
    private val _count = MutableStateFlow(0)
    val count = _count.asStateFlow()
    private val events = kotlinx.coroutines.channels.Channel<Unit>()
    init {
        scope.launch {
            for (e in events) _count.value++
        }
    }
    fun increment() { events.trySend(Unit) }
}

Pikakysymys

Miten actorit varmistavat tilan turvallisen samanaikaisen käytön?

Kertaus

Actorit kapseloivat muokattavan tilan yhteen korutiiniin ja tarjoavat toiminnot tyypitettyinä kanavaviesteinä. Ne sarjoittavat käytön luontevasti, tukevat tilakoneita ja poistavat lukituskilpailun — viestinvälityksen aiheuttaman ylimääräisen välikerroksen kustannuksella.

Aloita maksutta

Opi Kotlin tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
51
Oppitunnit
203

Usein kysytyt kysymykset

Onko oppitunti ”Actorit ja rakenteinen tilanhallinta” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Kotlin Academy-oppimispolun 3 oppituntia, myös oppitunnin “Actorit ja rakenteinen tilanhallinta”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Actorit ja rakenteinen tilanhallinta”?

Käyttäkää actor-tyylisiä malleja tilan käytön sarjallistamiseen samanaikaisessa koodissa. Harjoittelet Kotlin Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Kotlin Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Kotlin Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”Actorit ja rakenteinen tilanhallinta”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Kotlin Academy-oppitunnilla?

Kyllä. Jokainen Kotlin Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Channelin perusteet: send, receive ja close
  2. Channel-tyypit: Rendezvous, Buffered, Conflated, Unlimited
  3. Mutex ja Semaphore jaetulle tilalle
  4. Actorit ja rakenteinen tilanhallinta
← Takaisin: Kotlin Academy