Coroutines — kulcsfogalmak, Job és Dispatchers Kotlinban

Szerző: IT Sectr Megjelenés: 2026-03-16 Olvasási idő: 8 perc

A Coroutines (korutinok) könnyű Kotlin-szálak aszinkron programozáshoz, amelyek a kotlinx.coroutines könyvtáron keresztül érhetők el. A JetBrains Kotlin Documentation, 2026 szerint a Coroutines lehetővé teszi a függvény végrehajtásának felfüggesztését a szál blokkolása nélkül, ellentétben a hagyományos Thread-ekkel. A korutinok korlátozott szálkészleten futnak, ami ezerszer könnyebbé teszi őket a natív szálaknál. A Kotlin Coroutines teljes mértékben integrált az Android Jetpack, Retrofit, Room és más népszerű Android-ökoszisztéma könyvtárakkal.

Főbb pontok

  • Coroutines — könnyű Kotlin-szálak aszinkron kódhoz blokkolás nélkül
  • Suspend függvény — függvény, amely képes felfüggeszteni és folytatni a szál blokkolása nélkül
  • Dispatcher meghatározza a szálkészletet a korutin végrehajtásához
  • Job — a korutin leírója megszakítási és állapotkövetési támogatással
  • CoroutineScope kezeli a korutinok életciklusát és azok megszakítását befejezéskor

Mik a Kotlin-korutinok

Coroutines (korutinok) egy aszinkron programozási mechanizmus Kotlinban, amely a kotlinx.coroutines könyvtárban van implementálva. Az operációs rendszer szálaitól eltérően a korutinok nincsenek egy adott szálhoz kötve: felfüggeszthetők az egyik szálon és folytathatók egy másikon. Egyetlen szál több ezer korutint hajthat végre, minimális többletköltséggel váltva közöttük.

A korutinok a Kotlin 1.3-ban (2018) jelentek meg kísérleti funkcióként, és a Kotlin 1.5-ben (2021) váltak stabillá. A Coroutines a callback hell problémáját az async/await-hez hasonlóan oldja meg, de gazdagabb API-t kínál: csatornák (Channel), Flow, kivételkezelés a Job hierarchiában és közvetlen integráció az Android Lifecycle-lel.

A JetBrains (2025) szerint minden korutin körülbelül 100 bájt memóriát fogyaszt szemben a natív szál 1+ MB-jával. Ez lehetővé teszi több millió korutin futtatását egyetlen alkalmazásban OutOfMemoryError kockázata nélkül. Éppen a korutinok könnyűsége teszi őket az aszinkronitás előnyben részesített eszközévé Androidon.

Hogyan működnek a korutinok a motorháztető alatt

Minden Kotlin-korutin Continuation Passing Style (CPS) segítségével állapotgéppé fordul. A fordító egy rejtett Continuation paramétert ad minden suspend függvényhez. A Continuation tartalmazza a folytatási pontot és az összes lokális változót. Amikor a korutin felfüggesztődik, a runtime elmenti a Continuation-t, és amikor folytatódik, visszaállítja azt a Dispatcher készlet bármely elérhető szálán.

Suspend függvények: felfüggesztés és folytatás

suspend — a Kotlin kulcsszava, amely felfüggeszthetőként jelöli meg a függvényt. Egy ilyen függvény csak másik suspend függvényből vagy korutinból hívható meg. Egy suspend függvényen belül más suspend függvények tetszőleges sorrendben hívhatók, és minden hívási pont potenciális felfüggesztési pont.

A mechanika egyszerű: amikor egy suspend függvény meghív egy másik suspend függvényt, felfüggesztődik abban a pontban, felszabadítva a szálat. A meghívott függvény befejezése után a runtime az elmentett helyről folytatja a végrehajtást. Ezt cooperative cancellation-nek hívják — egyetlen szál sem blokkolódik.

  • Felfüggesztés — a korutin felszabadítja a szálat anélkül, hogy blokkolná
  • Folytatás — a korutin ugyanott folytatja, ahol felfüggesztődött
  • Szál — a korutin felfüggeszthető az A szálon és folytatható a B szálon
  • Kivételek — try/catch segítségével kezelhetők, akárcsak a szinkron kódban

Fontos: a suspend függvény alapértelmezetten nem aszinkron. A végrehajtás sorrendje szekvenciális marad, ha nem használnak launch vagy async függvényt. A suspend csak azt teszi lehetővé, hogy a függvény felfüggeszthető legyen a szál blokkolása nélkül, és a korutin kontextus részét képezze. A Continuation Passing Style egy olyan fordítási modell, amelyben minden suspend függvény kap egy rejtett Continuation callback-et, és a fordító egy állapotgépet generál a felfüggesztések és folytatások kezelésére.

CoroutineScope és Structured Concurrency

CoroutineScope egy kontextus, amely meghatározza a korutinok életciklusát. Minden korutint egy scopen belül kell elindítani. Amikor a scope megszakításra kerül (pl. egy Activity befejezésekor), az összes gyermek korutin automatikusan megszakításra kerül. Ez megakadályozza a háttérfeladatok szivárgását. Az Android Jetpack kész scope-okat biztosít minden komponenshez: viewModelScope a ViewModel-hez és lifecycleScope az Activity-hez és Fragment-hez, amelyek automatikusan megszakadnak a megfelelő komponens megsemmisülésekor.

Structured Concurrency — az az elv, amely garantálja, hogy egy korutin nem fejeződik be addig, amíg az összes gyermek korutinja be nem fejeződött. A Job hierarchia egy fát alkot: a gyökér korutin létrehoz egy parent job-ot, a gyermekek child job-okat hoznak létre. A parent job megszakítása az összes gyermekre terjed. A Structured Concurrency az alapvető különbség a korutinok és a szálak között.

ScopeHol használjákMegszakítás
GlobalScopeCsak démonfeladatokhozNem szakad meg automatikusan
viewModelScopeAndroid ViewModelViewModel tisztításakor
lifecycleScopeAndroid Activity/FragmentLifecycle megsemmisülésekor
coroutineScopeSuspend függvényen belülA parent job megszakításakor

SupervisorJob a hibakezeléshez

A szokásos Job egy gyermek korutin hibája esetén megszakítja az összes testvért. A SupervisorJob kivétel: az egyik gyermek korutin hibája nem befolyásolja a többit. Ez akkor fontos, amikor több független feladat fut párhuzamosan, és az egyik meghiúsulhat anélkül, hogy a többit meg kellene szakítani.

Dispatchers és korutin-építők

Dispatchers határozza meg, hogy a korutinok mely szálakon fussanak. Dispatchers.Main — az Android fő UI-szála. Dispatchers.IO — készlet blokkoló műveletekhez (hálózat, lemez). Dispatchers.Default — CPU-intenzív feladatokhoz. Dispatchers.Unconfined — az aktuális szálon indul, de nem garantálja, hogy ott is marad. A megfelelő Dispatcher kiválasztása kritikus a teljesítmény szempontjából: egy IO-feladat a Default-on blokkolja a készletet a számításokhoz, míg egy CPU-feladat az IO-n felesleges szálakat hoz létre.

withContext — a Dispatcher váltására szolgáló függvény a korutinon belül. Például egy JSON-t elemző suspend függvény átválthat Dispatchers.Default-re a számítás során, és visszatérhet Dispatchers.Main-re a UI frissítéséhez. A withContext a leggyakrabban használt építő az Android-fejlesztésben.

Három fő korutin-építő

launch — elindít egy korutint, visszaad egy Job-ot, nem ad vissza eredményt (fire-and-forget). async — elindít egy korutint, visszaad egy Deferred-et, amelyből az eredmény await-en keresztül érhető el. runBlocking — blokkolja az aktuális szálat a korutin végrehajtásához (csak tesztekhez és main függvényekhez). Az építő kiválasztása a forgatókönyvtől függ: a launch eseményekhez és frissítésekhez, az async eredménnyel járó feladatokhoz, a runBlocking csak tesztekhez vagy belépési pontokhoz alkalmas.

Kódpéldák korutinokkal Kotlinban

Vizsgáljunk meg három gyakorlati forgatókönyvet: alap korutin launch-al, párhuzamos hívás async-kal és hibakezelés SupervisorJob-bal.

Korutin indítása launch-al

viewModelScope.launch elindít egy korutint a ViewModel kontextusában. A ViewModel tisztításakor a korutin automatikusan megszakad.

kotlin
class ProfileViewModel : ViewModel() {
    fun loadUser() {
        viewModelScope.launch(Dispatchers.IO) {
            val user = api.fetchUser()
            withContext(Dispatchers.Main) {
                showUser(user)
            }
        }
    }
}

Párhuzamos kérések async-kal

coroutineScope async-kal három kérést indít párhuzamosan. Az eredmények a .await() segítségével gyűjthetők össze. Ha bármelyik kérés meghiúsul, az összes megszakad.

kotlin
suspend fun loadDashboard(): Dashboard = coroutineScope {
    val user = async { api.fetchUser() }
    val posts = async { api.fetchPosts() }
    val stats = async { api.fetchStats() }
    Dashboard(user.await(), posts.await(), stats.await())
}

Hibakezelés SupervisorJob-bal

SupervisorJob lehetővé teszi, hogy minden korutin függetlenül fejeződjön be. Az egyik kérés hibája nem szakítja meg a többit.

kotlin
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
    try { api.fetchUsers() } catch (e: Exception) { log(e) }
}
scope.launch {
    try { api.fetchPosts() } catch (e: Exception) { log(e) }
}

Korutinok vs szálak: összehasonlítás és forgatókönyvek

A szálak (Thread) az operációs rendszer primitívjei. Minden szálnak saját verme (~1 MB) van, és létrehozásához és váltásához rendszerhívás szükséges. A korutinok nyelvi primitívek, amelyek nem kötődnek az OS-hez. A Continuation-t használják az állapot mentésére, és futásidejű szinten váltanak rendszerhívások nélkül.

  • Memória — szál ~1 MB, korutin ~100 bájt. 10 000-szeres különbség
  • Létrehozás — szál ~1 µs syscall, korutin ~0.01 µs JVM szinten
  • Váltás — szál ~0.1 µs (syscall), korutin ~0.001 µs (continuation)
  • Maximum — több ezer szál vs millió korutin egy eszközön
  • Megszakítás — a szál nem szakítható meg kívülről (elavult Thread.stop), a korutin igen a Job.cancel() segítségével

A Google (2025) szerint a korutinok szálak helyetti használata 90–95%-kal csökkenti a memóriafogyasztást a háttérfeladatokhoz Android-alkalmazásokban. Az összes modern Android-könyvtár (Retrofit, Room, WorkManager) beépített támogatással rendelkezik a korutinokhoz suspend függvényeken keresztül. A Ktor (a JetBrains HTTP kliens keretrendszere) szintén teljes egészében korutinokra épül, minden kéréshez suspend-függvényeket biztosítva callback API nélkül. A Room támogatja a korutinokat a DAO-ban lévő suspend-függvényeken keresztül, lehetővé téve az adatbázis-lekérdezések végrehajtását a fő szál blokkolása nélkül.

Mikor használjunk szálakat a korutinok helyett

A szálak továbbra is szükségesek a natív kódhoz JNI-n keresztül, a blokkoló CPU-intenzív hívásokhoz időkorlát nélkül (videó renderelés, szimulációk) és a C könyvtárakkal való integrációhoz. Minden másra — korutinok.

Gyakran Ismételt Kérdések

Miben különbözik a korutin a száltól?

Korutin — egy felfüggeszthető munkaegység, amely egy meglévő szálon fut. A szál egy rendszererőforrás saját veremmel. A korutinok ezerszer könnyebbek a szálaknál, és nem blokkolják az erőforrásokat felfüggesztéskor.

Mi az a Dispatchers.IO és miben különbözik a Default-tól?

Dispatchers.IO blokkoló I/O műveletekhez (hálózat, fájlok) való, és szükség esetén új szálakat hozhat létre. Dispatchers.Default fix méretű készlettel rendelkezik (CPU-magok száma) CPU-intenzív számításokhoz.

Hogyan szakíthatok meg egy futó korutint?

Job.cancel() megszakítja a korutint és az összes gyermekét. A megszakítás ellenőrzéséhez a korutinon belül használja a ensureActive() függvényt — CancellationException-t dob, ha a korutin megszakításra került.

Használhatók a korutinok RxJava-val?

Igen — a kotlinx-coroutines-rx3 könyvtáron keresztül. Az awaitSingle, awaitFirst és más függvényeket biztosít az Observable/Single suspend függvényekké alakításához és vissza a flowable segítségével.

Mi a Flow a korutinokban?

Flow — egy hideg aszinkron adatfolyam, az RxJava Observable megfelelője korutinokhoz. A Flow szekvenciálisan bocsát ki értékeket, és kivétellel vagy sikerrel végződik. Támogatja a map, filter, catch és más operátorokat.

Összefoglalás

  • Coroutines — könnyű Kotlin-szálak felfüggesztéssel blokkolás nélkül, Continuation Passing Style segítségével
  • suspend — kulcsszó a felfüggeszthető függvények jelölésére
  • Dispatchers kezelik a szálkészletet: Main, IO, Default sorrendben
  • CoroutineScope a korutinok életciklusát a komponenshez köti (Activity, ViewModel)
  • launch korutint indít eredmény nélkül, async/await — eredménnyel
  • Structured Concurrency garantálja a gyermek korutinok hierarchikus megszakítását
  • Korutinok vs szálak — a korutinok 10 000-szer könnyebbek és az Android szabványai

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is