Vianoce v ITnetwork sú tu! Dobí si teraz kredity a získaj až 80 % extra kreditov na e-learningové kurzy ZADARMO. Zisti viac.
Hľadáme nové posily do ITnetwork tímu. Pozri sa na voľné pozície a pridaj sa k najagilnejšej firme na trhu - Viac informácií.

1. diel - Úvod do coroutines v Kotline

Vítam vás pri prvom dieli nášho tutoriálu zameraného na Kotlin coroutines. V priebehu nášho e-learningového kurzu si vysvetlíme, čo to sú coroutines ak čomu slúži a ukážeme si, ako s nimi pracovať.

Minimálne požiadavky kurzu

Pre úspešné absolvovanie súčasného kurzu je potrebná znalosť jazyka Kotlin na úrovni objektovo orientovaného programovania vrátane základných znalostí práce s kolekciami v Kotline. Výhodou je aj znalosť tvorby viacvláknových aplikácií.

Posledný odkaz vás presmeruje na Java kurz o práci s vláknami. K tej Kotlin môže použiť pripravené Java triedy alebo práve alternatívny spôsob tvorby asynchrónnych aplikácií pomocou coroutines.

V našom kurze si mnohokrát ukážeme príklad s použitím vlákien a druhý, ktorý využíva coroutines, aby sme mohli porovnať efektivitu oboch riešení.

Definícia coroutines a problém synchrónnosti

V oficiálnej Kotlin dokumentácii sú coroutines definované takto:

Coroutine je inštanciou pozastaviteľnej operácie. Je koncepčne podobná vláknu v tom zmysle, že na spustenie berie blok kódu, ktorý funguje súbežne so zvyškom kódu. Coroutine však nie je viazaná na žiadne konkrétne vlákno. Môže pozastaviť svoje vykonávanie v jednom vlákne a obnoviť ho v inom.

Coroutines možno považovať za ľahké vlákna, ale existuje množstvo dôležitých rozdielov, vďaka ktorým sa ich použitie veľmi líši od vlákien.

Aby sme túto definíciu a motív, prečo boli v Kotline coroutines vytvorené, pochopili, uveďme si jednoduchý príklad.

Motivačný príklad

Predstavme si súbor, kde máme uložené napríklad čísla v nasledujúcom formáte:

1
45
32
78
22

V našej aplikácii budeme chcieť sčítať všetky čísla v tomto súbore. Bez znalosti coroutines by sme postupovali tak, že by sme najprv načítali všetky čísla do kolekcie, ktorou by sme následne cyklom prechádzali a jednotlivé položky sčítali. Mohlo by sa zdať, že tento prístup je bez problémov, a do určitej miery takto naozaj môžeme v aplikácii pracovať. V niektorých prípadoch by ale takýto postup bol veľmi pomalý a aplikáciu by nadmerne zaťažoval.

Teraz sa určite pýtate, ako by sme mohli urýchliť vykonávanie programu, keď iba prečítame dáta zo súboru a následne ich sčítame. Postup je veľmi jednoduchý. Prečítame číslo zo súboru a súčasne ho pripočítame k už načítaným.

Rozdiel je nepatrný a pri jednoduchých úkonoch sa to môže zdať zbytočné, pretože to neprinesie oveľa väčší výkon programu. Avšak takýto postup sa už oplatí napríklad v prípade, že nejde o jednoduchú operáciu sčítania, ale napríklad nájdenie všetkých prvočísel v poli čísel. Táto operácia je totiž už o niečo náročnejšia na prostriedky počítača. Pokiaľ budeme hľadať prvočísla už pri načítaní dát, dokážeme tak výrazne zlepšiť výkon aplikácie.

Coroutines slúži práve na to, aby sme mohli zároveň čítať a spracovávať dáta.

V súvislosti s coroutines hovoríme o tzv. asynchrónnom programovaní. Tento termín označuje skutočnosť, kedy je kód v coroutine bloku vykonaný mimo beh hlavnej línie programu.

Prvý coroutine projekt v Kotline

Aby sme pochopili čo najlepšie celú problematiku, ukážeme si všetko na názornom príklade. Vytvoríme si prvý jednoduchý Coroutine projekt.

Otvoríme si IntelliJ IDEA, klikneme na New Project a otvorí sa nám nové okno, ktoré vyplníme nasledovne:

založenie nového projektu - Coroutines v Kotline

Potom klikneme na Create, tým sa nám vytvorí a otvorí nový projekt.

Pridanie závislosti

Coroutines implementuje externá knižnica kotlinx.coroutines. Než budeme môcť pokračovať, budeme ju musieť pridať ako novú závislosť do súboru build.gradle. Tým povieme kompilátoru, že budeme používať coroutines.

Otvoríme si súbor build.gradle a do bloku dependencies pridáme nasledujúci riadok kódu:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"

Výsledný súbor potom vyzerá nasledovne:

plugins {
   id 'org.jetbrains.kotlin.jvm' version '1.8.0'
   id 'application'
}

group = 'org.example'
version = '1.0-SNAPSHOT'

repositories {
   mavenCentral()
}

dependencies {
   testImplementation 'org.jetbrains.kotlin:kotlin-test'
   implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
}

test {
   useJUnitPlatform()
}

kotlin {
    jvmToolchain(8)
}

application {
   mainClassName = 'MainKt'
}

Iné balíčkovacie systémy majú nastavenie závislostí trochu odlišné. Rovnako sa postupne vyvíjajú ich nové verzie. Všetky potrebné informácie na pridanie coroutines do projektu sú uvedené voficiálnej dokumentácii knižnice.

Metóda main()

Máme pridané všetky závislosti a môžeme začať písať náš kód. Otvoríme si súbor Main.kt a upravíme v ňom metódu main(). Aby sme mohli pracovať s coroutines, prepíšeme ju nasledovne:

import kotlinx.coroutines.runBlocking

fun main(): Unit = runBlocking {

}

Čo znamená blok runBlocking si podrobnejšie vysvetlíme niekedy neskôr. Pre potreby dnešného dielu nám bude bohato stačiť, že sa jedná o most, ktorý nás dostane zo sveta bez coroutines do sveta s coroutines. Dnes si ukážeme a vysvetlíme plné základy coroutines, ktoré musíme pochopiť. Neskôr sa potom dostaneme aj k optimalizácii problémov, ktoré sme si uviedli na začiatku lekcie.

Launch a tvorba prvej coroutine

Poďme si teda vytvoriť našu prvú coroutine. Metódu main() doplníme nasledovne:

fun main(): Unit = runBlocking {
   launch {
       repeat(3) {
           println("Hello $it")
       }
   }
}

V kóde môžeme vidieť blok launch, ktorý slúži na tvorbu coroutines. Ako presne funguje, sa dozvieme za malý moment. Ak spustíme tento program, dostaneme nasledujúci výstup:

Hello 0
Hello 1
Hello 2

Nič neočakávaného pre väčšinu z nás.

Pridanie druhej coroutine

A čo sa stane, keď pridáme ďalší blok launch ? Môžeme si to hneď vyskúšať. Kód upravíme nasledovne:

fun main(): Unit = runBlocking {
   launch {
       repeat(3) {
           println("Hello $it")
       }
   }
   launch {
       repeat(3) {
           println("World $it")
       }
   }
}

Keď opäť program spustíme, dostaneme nasledujúci výstup:

Hello 0
Hello 1
Hello 2
World 0
World 1
World 2

Opäť nič divné. Teda na čo nám slúži launch a coroutines? Podobne ako pri práci s vláknami používame Thread, tak coroutines (a builder launch) slúžia k tomu, aby sme mohli tvoriť dve rutiny, ktoré bežia paralelne.

Medzi coroutines a Thread je jeden veľký rozdiel. Ak by sme vytvorili sto tisíc vlákien pomocou Thread, čoskoro by nám došla pamäť. Ale pokiaľ vytvoríme sto tisíc jednotlivých coroutines, tak nám väčšinou pamäť nedôjde. O coroutines je možné premýšľať ako o ľahkých vláknach, teda vláknach nenáročných na pamäť, procesor atď.

Blok launch nám slúži na to, aby sme vytvorili toto ľahké vlákno. Kód, ktorý sme pred chvíľkou napísali, spustia dve couroutines, ktoré dokážu bežať paralelne. Prečo teda výstup vyzerá, ako keby nebol kód vykonávaný paralelne? Tento problém je o niečo zložitejší a súvisí s tzv. dispatchers (dispečermi), o ktorých sa budeme rozprávať v ďalších dieloch.

Funkcia delay()

Aby sme dokázali, že coroutines beží paralelne, použijeme funkciu delay(). Ide o verziu metódy Thread.sleep(), ktorá je určená pre coroutines. Jednoducho zastavíme vykonávanie danej coroutine na uvedený počet milisekúnd. Kód upravíme nasledovne:

fun main(): Unit = runBlocking {
    launch {
        repeat(3) {
            println("Hello $it")
            // Pozastavit vykonávání programu na 0.5 s
            delay(500)
        }
    }
    launch {
        repeat(3) {
            println("World $it")
            // Pozastavit vykonávání programu na 1.5 s
            delay(1500)
        }
    }
}

Ak tento kód spustíme, dostaneme výstup podobný tomuto:

Hello 0
World 0
Hello 1
Hello 2
World 1
World 2

Zámerne som použil formuláciu "výstup podobný tomuto", pretože coroutines môžu bežať v rôznom poradí.

Na rekapituláciu dnešnej lekcie si zhrnieme jednotlivé informácie. Blok launch nám slúži na vytvorenie coroutine, teda ľahkého vlákna, ktoré môže bežať paralelne s hlavným vláknom. Funkcia delay() nám slúži na pozastavenie výkonu coroutine na zadaný počet milisekúnd. Toto nám bude pre dnešok stačiť.

V budúcej lekcii, Tvorba Coroutines v Kotline , si uvedieme jednotlivé možnosti tvorby coroutines a na praktických príkladoch si ukážeme, aký je medzi nimi rozdiel.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 2x (113.27 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Kotlin

 

Všetky články v sekcii
Coroutines v Kotline
Preskočiť článok
(neodporúčame)
Tvorba Coroutines v Kotline
Článok pre vás napísal Marek Urbańczyk
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje programování v Kotlinu, Javě. Má také zkušenosti s C#.
Aktivity