Ce face Corutine (ideea)
Un fir de execuție (thread) este scump: ocupă memorie, iar când așteaptă (după rețea, după un fișier) stă blocat. O corutină este o bucată de calcul care se poate suspenda la anumite puncte și poate fi reluată mai târziu, eventual pe alt fir. Mii de corutine pot rula pe câteva fire, pentru că o corutină suspendată nu ocupă niciun fir.
Limbajul Kotlin are doar cuvântul suspend și câteva funcții de bază. Un exemplu din biblioteca standard este sequence { }: blocul este o corutină care se oprește la fiecare yield(x), dă valoarea și continuă doar când îi ceri următorul element. Așa poți scrie generatoare, chiar și infinite, ca pe un cod obișnuit.
Pentru programe asincrone (aplicații Android, servere Ktor) se folosește biblioteca kotlinx.coroutines: launch pornește o corutină, async/await calculează un rezultat în paralel, delay așteaptă fără să blocheze, iar runBlocking leagă codul obișnuit de lumea corutinelor. Biblioteca nu este instalată pe serverul care verifică exemplele, deci exemplul 3 are rezultatul scris de mână.
Exemple
Exemplul 1: sequence: o corutină care se oprește la yield
Mesajele arată ordinea reală: corutina rulează doar până la următorul yield, apoi controlul revine în for.
fun main() {
val pasi = sequence {
println(" [corutina] pregătesc comanda")
yield("comanda pregătită")
println(" [corutina] împachetez")
yield("colet împachetat")
println(" [corutina] trimit spre Iași")
yield("colet trimis")
}
for (stare in pasi) {
println("main a primit: $stare")
}
}
Rezultat
[corutina] pregătesc comanda
main a primit: comanda pregătită
[corutina] împachetez
main a primit: colet împachetat
[corutina] trimit spre Iași
main a primit: colet trimis
Exemplul 2: Generator infinit
Bucla while (true) nu blochează programul: corutina se suspendă la fiecare yield, iar take(10) cere doar zece valori.
fun main() {
val fibonacci = sequence {
var a = 0L
var b = 1L
while (true) {
yield(a)
val urmator = a + b
a = b
b = urmator
}
}
println(fibonacci.take(10).toList())
println(fibonacci.first { it > 1000 })
}
Rezultat
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
1597
Exemplul 3: launch și delay cu kotlinx.coroutines (nu rulează aici)
Cele trei corutine pornesc aproape simultan pe același fir și așteaptă cu delay, fără să-l blocheze. Mesajele apar în ordinea timpilor de așteptare, iar programul durează aproximativ 300 ms, nu 600.
import kotlinx.coroutines.*
fun main() = runBlocking {
for ((oras, ms) in listOf("Iași" to 300L, "Cluj" to 100L, "Arad" to 200L)) {
launch {
delay(ms)
println("Vremea în $oras a sosit după $ms ms")
}
}
println("Am pornit toate cererile")
}
Rezultat (cu kotlinx.coroutines instalată)
Am pornit toate cererile
Vremea în Cluj a sosit după 100 ms
Vremea în Arad a sosit după 200 ms
Vremea în Iași a sosit după 300 ms
Greșeli frecvente
- Confuzia dintre corutine și fire: o corutină nu este un fir. Dacă în ea faci o operație care blochează (
Thread.sleep, citire sincronă), blochezi firul pe care rulează. - Căutarea lui
launchsaurunBlockingîn biblioteca standard: sunt în kotlinx.coroutines, care se adaugă separat (Gradle). - Apelul unei funcții
suspendstrăine însequence { }: blocul este restricționat și acceptă doaryield/yieldAll.
Comenzi înrudite
suspend: suspend marchează o funcție care se poate opri (suspenda) fără să blocheze firul și poate continua mai târziu; se apelează din corutine.sequence: Construiește o secvență dintr-un bloc de cod care dă valori una câte una cu yield; codul se oprește și continuă la cerere.generateSequence: Creează o secvență în care fiecare element se calculează din cel anterior; se oprește când funcția întoarce null.lazy: Creează o valoare calculată abia la prima folosire, apoi păstrată; se folosește cu val nume by lazy { … }.