Kotlin の Coroutine を概観する


Kotlin では バージョン 1.1 から Coroutine が導入されています。

検索すると coroutine, async/await に関する情報がたくさん見つかりますが、そもそも coroutine がどのようなもので、どのように使うのかを簡単に説明した資料が少なかったため、 [Kotlin 公式ドキュメント](https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html)を元に勉強してみました。

筆者は Coroutine 自体に馴染みがなく、今回 Kotlin で初めて触れています。根本的な認識の誤り等なにかございましたらご指摘いただければ嬉しく思います。

※ 2017年11月現在の最新版は 1.1.60 ですが、 Coroutine は未だ experimental となっています。そのため今後破壊的な変更が行われる可能性があります。

# Coroutine は軽量な Thread

Coroutine とは、つまるところ軽量な Thread のようなものです。
Thread のように他の処理をブロックすることなく並列に処理を行えます。
しかしとても軽量です。
Thread はその開始と終了に無視できないコストがかかるのに対して、 Coroutine はほとんど気にする必要のない程度のコストしかかかりません。
同時に数千数万の Coroutine を容易に実行することができます。

# launch

Coroutine を作成して利用するのはとても簡単です。

```kotlin
launch {
    // 何らかの処理
}
```

これだけでラムダ内の何らかの処理をその実行スレッドをブロックすることなく並列に処理することができます。

次のプログラムを実行するとどうなるでしょうか？

```kotlin
fun main(args: Array<String>) {
    println("start")
    launch {
        println("coroutine!")
    }
    println("end")
}
```

実はこれでは次のように出力されてしまいます。

```
start
end
```

launch 内の処理が実行されていません。
なぜなら、 launch はその実行スレッドを全くブロックしないからです。
`"start"` が出力され、 `launch` が実行された直後にすぐに `"end"` が出力されてプログラムは終了します。
launch のラムダが実行されるよりも先にプログラムが終了しているのです。

```kotlin
fun main(args: Array<String>) {
    println("start")
    launch {
        println("coroutine!")
    }
    Thread.sleep(1000)
    println("end")
}
```

このようにすると、期待通り次のように出力されます。

```
start
coroutine!
end
```

launch のラムダ内の処理が完了するまでの十分な時間だけスレッドを停止させてから、 `"end"` と出力して終了するためです。

# runBlocking

`runBlocking` はその名の通り、現在のスレッドをブロックします。

先程のプログラムを以下のように変更します。

```kotlin
fun main(args: Array<String>) {
    println("start")
    runBlocking {
        println("coroutine!")
    }
    println("end")
}
```

実行すると以下のように出力されます。

```
start
coroutine!
end
```

Corotuine が終了するまで待ってから、 `"end"` が出力されることがわかります。

また、 `runBlocking` は実行した Coroutine の戻り値を取得することもできます。

次のように変更します。

```kotlin
fun main(args: Array<String>) {
    println("start")
    val text = runBlocking {
        "coroutine!"
    }
    println(text)
    println("end")
}
```

この場合も先程と同じく、`"start"`, `"coroutine!"`, `"end"` の順に出力されます。

ちなみに `runBlocking` は Coroutine 内から呼び出すべきではありません。
従来型のブロッキング処理と Coroutine で実装された処理の橋渡しとしてや、例のように main 関数内での使用、またはテストでの使用などを想定して設計されているためです。

# Job#join()

`runBlocking` は現在のスレッドをブロックするため、コルーチンとスレッドの境界でしか使用すべきではありません。
コルーチン内で他のコルーチンの終了を待機するために `Job#join` が利用できます。
`launch` は戻り値に `Job` インスタンスを返すため、上の例は次のように変更できます。

```kotlin
fun main(args: Array<String>) = runBlocking() {
    println("start")
    launch {
        println("coroutine!")
    }.join()
    println("end")
}
```

# async/await

`runBlocking` を使用すれば、 Coroutine の戻り値を取得することができました。
しかし、現在のスレッドをブロックして Coroutine の戻り値を得られるというのは非同期処理を行いたい場合はあまり嬉しくありません。
`launch {}.join()` のように他のコルーチンを非ブロッキングで中断し、かつ `runBlocking` のように終了時に戻り値を受け取りたい場合はどうすればよいでしょうか。

## async と Deferred&lt;T&gt;
そこで `async` の出番です。
`async` は Coroutine を作成し、戻り値として `Deferred<T>` 型のオブジェクトを返します。
`Deferred<T>` 型は future のようなもので、 Coroutine の処理が終わったタイミングでその戻り値を取得することができます。

まず、先程からの例を次のように変更します。

```kotlin
fun main(args: Array<String>) {
    println("start")
    async {
        println("coroutine!")
    }
    println("end")
}
```

しかしこれでは `"coroutine!"` は出力されません。 `launch` の時と同じく Coroutine が実行され `"coroutine!"` が出力されるよりも先にプログラムが終了しているためです。

## await

やはり Coroutine の終了を待つ必要があります。
`async` の返す `Deferred<T>` インタフェースには `await` メソッドが定義されています。
`await` メソッドは、 `async` が起動したコルーチンの終了まで現在のコルーチンを中断し、終了したコルーチンの戻り値を取得します。

```kotlin
fun main(args: Array<String>) = runBlocking() {
    println("start")
    val text = async {
        "coroutine!"
    }.await()
    println(text)
    println("end")
}
```

## Suspending Function

ところで、 `join` メソッドや `await` メソッドのような 、Coroutine を中断させることのできる関数を `Suspending Function` と言います。
`suspend` modifier を付けて関数を宣言することで、その関数が Coroutine を中断可能であることを示すことができます。

```kotlin
suspend fun doSomething() {
    delay(1000)
    println("something")
}
```

この例では `doSomething` 関数は 1 秒待ってから `"something"` と出力します。
`delay` も Suspending Function として定義されており、その Coroutine の処理を指定した時間だけ中断（停止）させることができます。

`suspend` と宣言された関数は Coroutine 内または他の Suspending Function 内からしか呼び出すことができません。
また Coroutine を開始するには最低 1 つの Suspending Function がなければなりません。
先程から `launch` や `runBlocking` ではラムダを使用して Coroutine を作成していましたが、実はこれらは無名の Suspending Lambda だったのです。

launch のスキーマは次のようになっています。

```kotlin
public fun launch(
    context: CoroutineContext = DefaultDispatcher,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job
```

第3引数が匿名 Suspending Lambda になっています。

これらを踏まえた上で、次のようにプログラムを変更します。

```kotlin
fun main(args: Array<String>) {
    println("start")
    runBlocking {
        println("coroutine start")
        greet()
        println("coroutine end")
    }
    println("end")
}

suspend fun greet() {
    delay(1000)
    println("Hello, Coroutine!")
}
```

`"start"`, `"coroutine start"` とすぐに表示され、 1 秒間経ってから `"Hello, Coroutine!"`, `"coroutine end"`, `"end"` と順に出力されます。

定義した `greet()` 関数が runBlocking で作成した Coroutine の処理を中断し、再開しているのがわかります。

# まとめ

- Coroutine は軽量な Thread と考えることができる
- Coroutine を作成するコストは Thread とは比較できないほど軽微である
- `launch`, `runBlocking`, `async` などを用いて Coroutine を開始できる
- `runBlocking` では同期的に、 `async` では非同期的に Coroutine の戻り値を取得できる
- Suspending Function とは Coroutine を中断可能な関数のことである
- Coroutine を実行するには Suspending Function が必要である
- `launch`, `runBlocking`, `async` などは引数に匿名 Suspending Funtion を取ることで Coroutine を開始している
- Suspending Function は Coroutine か Suspending Function からしか実行できない
- Coroutine 楽しい！

# 参考
- https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html
- https://kotlinlang.org/docs/reference/coroutines.html?q=&p=0#coroutines