IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 1 - Úvod do coroutines v Kotlin

Vítám vás u prvního dílu našeho tutoriálu zaměřeného na Kotlin coroutines. V průběhu našeho e-learningového kurzu si vysvětlíme, co to jsou coroutines a k čemu slouží a ukážeme si, jak s nimi pracovat.

Minimální požadavky kurzu

Pro úspěšné absolvování stávajícího kurzu je třeba znalost jazyka Kotlin na úrovni objektově orientovaného programování včetně základních znalostí práce s kolekcemi v Kotlin. Výhodou je i znalost tvorby vícevláknových aplikaci.

Poslední odkaz vás přesměruje na Java kurz o práci s vlákny. K té Kotlin může použít připravené Java třídy nebo právě alternativní způsob tvorby asynchronních aplikací pomocí coroutines.

V našem kurzu si mnohde ukážeme příklad s použitím vláken a druhý, který využívá coroutines, abychom mohli porovnat efektivitu obou řešení.

Definice coroutines a problém synchronnosti

V oficiální Kotlin dokumentaci jsou coroutines definovány takto:

Coroutine je instancí pozastavitelné operace. Je koncepčně podobná vláknu v tom smyslu, že ke spuštění bere blok kódu, jenž funguje souběžně se zbytkem kódu. Coroutine však není vázána na žádné konkrétní vlákno. Může pozastavit své provádění v jednom vlákně a obnovit jej v jiném.

Coroutines lze považovat za lehká vlákna, ale existuje řada důležitých rozdílů, díky kterým se jejich použití velmi liší od vláken.

Abychom tuto definici a motiv, proč byly v Kotlin coroutines vytvořeny, pochopili, uveďme si jednoduchý příklad.

Motivační příklad

Představme si soubor, kde máme uložená například čísla v následujícím formátu:

1
45
32
78
22

V naší aplikaci budeme chtít sečíst všechna čísla v tomto souboru. Bez znalosti coroutines bychom postupovali tak, že bychom nejprve načetli všechna čísla do kolekce, kterou bychom následně cyklem procházeli a jednotlivé položky sčítali. Mohlo by se zdát, že tento přístup je bez problému, a do určité míry takto opravdu můžeme v aplikaci pracovat. V některých případech by ale takový postup byl velmi pomalý a aplikaci by nadměrně zatěžoval.

Teď se určitě ptáte, jak bychom mohli urychlit vykonávaní programu, když pouze přečteme data ze souboru a následně je sečteme. Postup je velice jednoduchý. Přečteme číslo ze souboru a současně ho přičteme k již načteným.

Rozdíl je nepatrný a u jednoduchých úkonů se to může zdát zbytečné, protože to nepřinese o moc větší výkon programu. Avšak takový postup se už vyplatí například v případě, že se nejedná o jednoduchou operaci sčítání, ale třeba nalezení všech prvočísel v poli čísel. Tato operace je totiž už o něco náročnější na prostředky počítače. Pokud budeme hledat prvočísla již při načítání dat, dokážeme tak výrazně zlepšit výkon aplikace.

Coroutines slouží právě k tomu, abychom mohli zároveň číst a zpracovávat data.

V souvislosti s coroutines mluvíme o tzv. asynchronním programování. Tento termín označuje skutečnost, kdy je kód v coroutine bloku vykonán mimo běh hlavní linie programu.

První coroutine projekt v Kotlin

Abychom pochopili co nejlépe celou problematiku, ukážeme si vše na názorném příkladu. Vytvoříme si první jednoduchý coroutine projekt.

Otevřeme si IntelliJ IDEA, klikneme na New Project a otevře se nám nové okno, které vyplníme následovně:

založení nového projektu - Coroutines v Kotlin

Poté klikneme na Create, tím se nám vytvoří a otevře nový projekt.

Přidání závislosti

Coroutines implementuje externí knihovna kotlinx.coroutines. Než budeme moci pokračovat, budeme ji muset přidat jako novou závislost do souboru build.gradle. Tím řekneme kompilátoru, že budeme používat coroutines.

Otevřeme si soubor build.gradle a do bloku dependencies přidáme následující řádek kódu:

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

Výsledný soubor pak vypadá následovně:

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

Jiné balíčkovací systémy mají nastavení závislostí trochu odlišné. Stejně tak se postupně vyvíjejí jejich nové verze. Všechny potřebné informace k přidání coroutines do projektu jsou uvedeny v oficiální dokumentaci knihovny.

Metoda main()

Máme přidané veškeré závislosti a můžeme začít psát náš kód. Otevřeme si soubor Main.kt a upravíme v něm metodu main(). Abychom mohli pracovat s coroutines, přepíšeme ji následovně:

import kotlinx.coroutines.runBlocking

fun main(): Unit = runBlocking {

}

Co znamená blok runBlocking si podrobněji vysvětlíme někdy později. Pro potřeby dnešního dílu nám bude bohatě stačit, že se jedná o most, který nás dostane ze světa bez coroutines do světa s coroutines. Dnes si ukážeme a vysvětlíme plné základy coroutines, které musíme pochopit. Později se pak dostaneme i k optimalizaci problémů, které jsme si uvedli na začátku lekce.

Launch a tvorba první coroutine

Pojďme si tedy vytvořit naši první coroutine. Metodu main() doplníme následovně:

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

V kódu můžeme vidět blok launch, který slouží ke tvorbě coroutines. Jak přesně funguje, se dozvíme za malý moment. Pokud spustíme tento program, dostaneme následující výstup:

Hello 0
Hello 1
Hello 2

Nic neočekávaného pro většinu z nás.

Přidání druhé coroutine

A co se stane, když přidáme další blok launch? Můžeme si to hned vyzkoušet. Kód upravíme následovně:

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

Když opět program spustíme, dostaneme následující výstup:

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

Opět nic divného. Tedy k čemu nám slouží launch a coroutines? Podobně jako při práci s vlákny používáme Thread, tak coroutines (a builder launch) slouží k tomu, abychom mohli tvořit dvě rutiny, které běží paralelně.

Mezi coroutines a Thread je jeden veliký rozdíl. Pokud bychom vytvořili sto tisíc vláken za pomocí Thread, brzo by nám došla paměť. Ale pokud vytvoříme sto tisíc jednotlivých coroutines, tak nám většinou paměť nedojde. O coroutines lze přemýšlet jako o lehkých vláknech, tedy vláknech nenáročných na paměť, procesor atd.

Blok launch nám slouží k tomu, abychom vytvořili toto lehké vlákno. Kód, který jsme před chvilkou napsali, spustí dvě couroutines, které dokáží běžet paralelně. Proč tedy výstup vypadá, jak kdyby nebyl kód vykonáván paralelně? Tento problém je o něco složitější a souvisí s tzv. dispatchers (dispečery), o kterých si budeme povídat v dalších dílech.

Funkce delay()

Abychom dokázali, že coroutines běží paralelně, použijeme funkci delay(). Jedná se o verzi metody Thread.sleep(), která je určená pro coroutines. Jednoduše zastavíme vykonávání dané coroutine na uvedený počet milisekund. Kód upravíme následovně:

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

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

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

Záměrně jsem použil formulaci "výstup podobný tomuto", protože coroutines mohou běžet v různém pořadí.

Pro rekapitulaci dnešní lekce si shrneme jednotlivé informace. Blok launch nám slouží k vytvoření coroutine, tedy lehkého vlákna, které může běžet paralelně s hlavním vláknem. Funkce delay() nám slouží k pozastavení výkonu coroutine na zadaný počet milisekund. Toto nám bude pro dnešek stačit.

V příští lekci, Tvorba coroutines v Kotlin, si uvedeme jednotlivé možnosti tvorby coroutines a na praktických příkladech si ukážeme, jaký je mezi nimi rozdíl.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 2x (113.27 kB)
Aplikace je včetně zdrojových kódů v jazyce Kotlin

 

Všechny články v sekci
Coroutines v Kotlin
Přeskočit článek
(nedoporučujeme)
Tvorba coroutines v Kotlin
Článek pro vás napsal Marek Urbańczyk
Avatar
Uživatelské hodnocení:
5 hlasů
Autor se věnuje programování v Kotlinu, Javě. Má také zkušenosti s C#.
Aktivity