Kotlin Academy · Lektion

Actors og struktureret tilstandshåndtering

Brug mønstre i actor-stil til at serialisere adgang til tilstand i samtidig kode.

Lektion 4 af 413 trin

Actors og struktureret tilstandshåndtering er en gratis Kotlin Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Kotlin Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Kotlin Academy-kurset indeholder 4 lektioner i alt.

Hvad er en aktør?

En aktør er en coroutine, der ejer privat, foranderlig tilstand og kommunikerer via en kanal. Ekstern kode får aldrig direkte adgang til tilstanden — kun via meddelelser.

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)
        }
    }
}

Brug af aktøren

Send meddelelser til aktøren med send. Aktøren behandler dem én ad gangen og serialiserer dermed adgangen til tilstanden.

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()
}

Aktøren erstatter Mutex

Aktører fjerner behovet for mutexer ved at gøre adgangen til tilstanden sekventiel som en del af designet. Alle ændringer sker i én coroutine.

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()
}

Typet meddelelsesprotokol

Definér din meddelelsesprotokol som et hierarki af sealed-klasser, så aktørens when-udtryk kan håndtere alle tilfælde.

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

Tilstandsmaskine som aktør

Aktører er naturlige tilstandsmaskiner: Interne tilstandsovergange sker atomisk som reaktion på meddelelser.

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()-builder

Builderen actor { } opretter en aktør med en kanal som indbakke. En aktør er en korutine, der behandler meddelelser fra channel.

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
}

Stop af en aktør

Luk aktørens sendekanal med close(). For-løkken i aktøren afsluttes, så der er mulighed for oprydning, før korutinen afsluttes.

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)
}

Aktører med tilbagetryk

Angiv aktørens kanal-kapacitet for at anvende tilbagetryk — afsendere sættes på pause, når indbakken er fuld.

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()
}

Ydeevne: aktør kontra Mutex

Aktører serialiserer adgang uden låsekonkurrence og egner sig til kompleks tilstand med flere meddelelsestyper. Mutex er enklere til en enkelt delt tæller, men er mindre let at kombinere.

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 til anmodning-svar

I mønstre med anmodning og svar inde i en aktør skal du inkludere en CompletableDeferred i meddelelsen. Aktøren færdiggør den, og afsenderen afventer den.

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])
    }
}

Moderne alternativ: StateFlow + coroutineScope

Builderen actor er eksperimentel og kan blive udfaset. Det moderne alternativ er en ViewModel- eller serviceklasse med en MutableStateFlow, der opdateres fra en enkelt korutine.

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) }
}

Hurtigt tjek

Hvordan sikrer aktører sikker samtidig adgang til tilstand?

Opsummering

Aktører indkapsler muterbar tilstand i en enkelt korutine og eksponerer handlinger som typede kanalmeddelelser. De serialiserer naturligt adgangen, understøtter tilstandsmaskiner og eliminerer låsekonkurrence — på bekostning af indirekte meddelelsesudveksling.

Gratis at komme i gang

Lær Kotlin med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
51
Lektioner
203

Ofte stillede spørgsmål

Er lektionen “Actors og struktureret tilstandshåndtering” gratis?

Ja — alle 3 lektioner i læringssporet Kotlin Academy, inklusive “Actors og struktureret tilstandshåndtering”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Kotlin Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Actors og struktureret tilstandshåndtering”?

Brug mønstre i actor-stil til at serialisere adgang til tilstand i samtidig kode. Du øver dig i Kotlin Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Kotlin Academy?

Der kræves ingen tidligere erfaring. Kotlin Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “Actors og struktureret tilstandshåndtering”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Kotlin Academy-lektion?

Ja. Alle Kotlin Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Grundlæggende om Channel: send, receive og close
  2. Channel-typer: Rendezvous, Buffered, Conflated, Unlimited
  3. Mutex og Semaphore til delt tilstand
  4. Actors og struktureret tilstandshåndtering
← Tilbage til Kotlin Academy