0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Kotlinコルーチン入門

Posted at

Kotlinコルーチン入門!

こんにちは。今回はKotlinの「コルーチン(coroutine)」について学んだことを整理しながらまとめました。理解が深まったポイントを中心に紹介します。

また、今回は以下の公式ドキュメントをもとに学習し、この記事で出てくるコードはそこから引用しています。

また、Kotlin Playgroundで簡単に実行できるので、手を動かしながら試してみることもできます。


キーワード

  • コルーチン
  • runBlocking{}
  • launch{}
  • async{}
  • await()
  • 構造化された並行性(structured concurrency)

✅ コルーチンとは?

並行処理(非同期)を簡単かつ安全に書くための仕組み。
複雑なスレッド処理を意識せずに、非同期処理が扱える。

  • 非同期:今の処理が終わらなくても次の処理が始まる
  • 同期:1つずつ順番に処理が終わるまで待つ

アプリではほとんどが非同期処理(コルーチン)になっています。
例えば、仮に同期処理だと、複雑な処理が終わるまでユーザーには次の画面が見えないなどの問題が発生します。
並列で処理を回すことで、裏で処理をしていると同時にユーザーには次の画面を反映させることが可能になります。


✅ 同期処理

//同期処理
fun main() {
    println("Weather forecast")
    println("Sunny")
}

このコードでは上から順番にprintlnが動いていく典型的な同期処理コードです。

//非同期処理(コルーチン)A
import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        delay(1000)
        println("Sunny")
    }
}

このコードでは上記と違い、runBlocking{}delay()が登場しました。

  • runBlocking{}:コルーチンのエントリポイント。ここからコルーチンだよ、という入り口です。
  • delay():指定した時間だけ処理を一時停止する suspend 関数。コルーチン内でのみ使用できます。
//非同期処理(コルーチン)A'
import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        printForecast()
    }
}

suspend fun printForecast() {
    delay(1000)
    println("Sunny")
}

この様にmain()の外で新しくsuspend関数として定義し、呼び出すこともできます。suspendを付けていないとエラーが出ます。
⇒suspend 関数はコルーチンまたは別の suspend 関数からしか呼び出せないため

非同期とはいえ、今のコードでは結局1つの処理が終わるまで待っているので、同期処理と大差ない結果になります。

✅ 非同期処理

//非同期処理B
import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        launch {
            printForecast()
        }
        launch {
            printTemperature()
        }
        println("Have a good day!")
    }
}

suspend fun printForecast() {
    delay(1000)
    println("Sunny")
}

suspend fun printTemperature() {
    delay(1000)
    println("30\u00b0C")
} 

ここで新たに登場するのがlaunch{}です。

  • launch{}:非同期処理を開始するための関数。結果を返さず、主に「実行」だけしたい処理に使います。

そうすると、このような順番で出力されます。

//Bの出力
Weather forecast
Have a good day!
Sunny
30°C

この様な順番で出力されると思います。ここでポイントなのが
Have a good day!が2番目に出力されている点です。
printForecast()printTemperature()はどちらも1秒待つ操作が組み込まれているため、println("Have a good day!")が即出力されたということです。

//非同期処理C
import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        val forecast: Deferred<String> = async {
            getForecast()
        }
        val temperature: Deferred<String> = async {
            getTemperature()
        }
        println("${forecast.await()} ${temperature.await()}")
        println("Have a good day!")
    }
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(1000)
    return "30\u00b0C"
}

次に登場するのがasync{}await()です。

  • async{}:非同期で処理を実行し、結果(値)を返すコルーチンを開始します。
  • await():async が返す結果(Deferred)を待ち、値を取得します。
    ⇒ つまり「結果を返す非同期処理」がしたいときに使うのが async。

なので、今回のコードCを実行すると

//Cの出力
Weather forecast
Sunny 30°C
Have a good day!

Have a good day!が最後に出力されました。
forecasttemperatureの2つは並列処理がされていますが、これらが終了するまで、println("Have a good day!")は待っている状況です。
しかし今のままではコードの意図が分かりにくく感じるかもしれません。

ここで 構造化された並行性(structured concurrency) という概念が登場します。
構造化された並行性とは、複数のコルーチン(非同期処理)を1つのスコープ内で起動し、まとめて管理・終了させる設計手法です。

これにより、コルーチンの開始や終了、例外処理の流れを制御しやすくなり、 安全で読みやすい非同期コードを書くことができます。

//非同期処理C'
import kotlinx.coroutines.*

fun main() {
    runBlocking {
        println("Weather forecast")
        println(getWeatherReport())
        println("Have a good day!")
    }
}

suspend fun getWeatherReport() = coroutineScope {
    val forecast = async { getForecast() }
    val temperature = async { getTemperature() }
    "${forecast.await()} ${temperature.await()}"
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(1000)
    return "30\u00b0C"
}

上記のコードCをリファクタリングしました。具体的にはmain()内でごちゃごちゃ定義していたforecasttemperatureをmain()関数の外で新しくsuspend関数としてgetWeatherReport()と定義しています。

結果的にmain()関数内を見るとどうでしょうか。
あたかも同期処理のコードAを彷彿とさせる見やすいコードになったのではないでしょうか。


まとめ

今回は非同期処理(コルーチン)について基礎的なものをまとめました。次は「例外とキャンセル」、「コルーチンのコンセプト」についてまとめたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?