コルーチンの基礎

※ソース記事は[こちら](https://kotlinlang.org/docs/coroutines-basics.html)
この章では、基礎的なコルーチンの概要を範囲としている。
# 初めてのコルーチン
コルーチンは、中断可能な計算結果のインスタンスである。それはコードのブロックを、コードの残りと並列に動作させるという意味で、概念的にはスレッドに近い。しかし、コルーチンは特定のスレッドに結びついていない。あるスレッドで、実行を一時中断し、他のスレッドで再開することが可能である。
コルーチンは軽量なスレッドとして考えることができるが、現実の使い方において、スレッドとはかなり異なる、多くの重要な点が存在する。
初めての動作するコルーチンとして次のコードを実行してみる。
```Kotlin
import kotlinx.coroutines.*

fun main() = runBlocking { // thisはCoroutineScope
    launch { // 新しいコルーチンを起動して続行する
        delay(1000L) // 1秒間ノンブロッキングな遅延 (デフォルトの時間単位はミリ秒)
        println("World!") // 遅延の後で表示
    }
    println("Hello") // メインのコルーチンは、前のものが遅延している間、続行する
}
```
次のような結果が表示されるだろう。
```
Hello
World!
```
このコードが何をするのか、解剖していこう。
[launch](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html)は、コルーチンビルダーである。これは残りのコードと並列に新しいコルーチンを起動し、独立して動作し続ける。それが`Hello`が最初に表示された理由である。
[delay](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/delay.html)は特別なsuspend関数である。それは指定した時間、コルーチンを中断する。コルーチンを中断することは、その潜在的なスレッドをブロックせず、他のコルーチンでそのコードのために潜在的なスレッドを実行し、使うことができる。
[runBlocking](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html)も、コルーチンビルダーであり、一般的な`fun main()`のコルーチン外の世界と、`runBlocking { ... }`の中カッコの内部のコルーチンのコードとの橋渡しをする。これは、`runBlocking`の中カッコの開始の直後で、`this: CoroutineScope`ヒントにより、IDEで強調表示される。
もし、コード内で`runBlocking`を削除あるいは、つけ忘れると、[lauch](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html)呼び出しで、エラーが表示されるだろう。なぜなら、`launch`は[CoroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html)でのみ、宣言されるからである。
```
Unresolved reference: launch
```
`runBlocking`の名前は、それを実行するスレッド(このケースではメインスレッド)が、`runBlocking { ... }`の内側のすてのコルーチンが実行を完了するまで、呼び出しの間ブロックされるという意味である。`runBlocking`がアプリケーションの最上位レベルで使われ、実際のコードの内部ではほとんど使われないのを度々見るだろうが、スレッドは高価なリソースであり、ブロッキングするのは非効率であり、大抵望ましくない。

## 構造化された並行性
コルーチンは、構造化された並列性の原則に従っており、それは、新しいコルーチンは、コルーチンの生存期間を決める、特定の[CoroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html)でのみ起動する、という意味である。上の例では、[runBlocking](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html)は、対応するスコープを確立していることを示しており、それが、前のサンプルが、1秒遅延した後で、`World`が表示されるまで待機し、そのあとでのみ終了する理由である。
実際のアプリケーションでは、たくさんのコルーチンが起動されるだろう。構造化された並行性により、それらが失われたり、漏れたりしないことが保証される。外側のスコープは、すべての子のコルーチンが完了するまで完了することができない。構造化された並行性により、すべてのエラーは適切に報告され、決して失われないことも、保証されている。
# 関数を抽出リファクタリング
`launch { ... }`の内部のコードブロックを分割した関数に抽出してみよう。このコードにおいて「関数を抽出」リファクタリングするときは、新しい関数`suspend`修飾子をつける。これが初めてのsuspend関数である。suspend関数は、コルーチンの内側で、単に通常の関数のように使うことができるが、追加の機能として、今度はコルーチンの実行を中断するための(この例の`delay`のような)他のsuspend関数を使うことができる。
```Kotlin
import kotlinx.coroutines.*

fun main() = runBlocking { // thisはCoroutineScope
    launch { doWorld() }
    println("Hello")
}

// これは最初のsuspend関数
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}
```
# スコープビルダー
異なるビルダーにより、提供されるコルーチンスコープに加え、[CoroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html)を使う、独自のスコープを宣言することができる。それはコルーチンスコープを作り、すべての起動した子が完了するまで完了しない。
[runBlocking](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html)と[coroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html)のビルダーは、両方とも本体とすべての子が完了するまで待つので、似ているように見えるかもしれない。主な違いは、[runBlocking](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html)メソッドは待つための現在のスレッドを待つ一方で、[coroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html)は、他が利用するために配下のスレッドを中断し、解放するだけである。この違いのため、[runBlocking](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html)は通常の関数であり、[coroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html)はsuspend関数である。
```Kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    doWorld()
}

suspend fun doWorld() = coroutineScope {  // thisはCoroutineScope
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello")
}
```
このコードも次のような出力になる。
```
Hello
World!
```
# スコープビルダーと並列性
[coroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html)ビルダーは、複数の並列操作を実行するため、内部にどんなsuspend関数を使うことができる。`doWorld`suspend関数内部に二つの並列コルーチンを起動しよう。
```Kotlin
import kotlinx.coroutines.*

// doWorld、続いて"Done"を順次実行する
fun main() = runBlocking {
    doWorld()
    println("Done")
}

// 並列で両方のセクションを実行する
suspend fun doWorld() = coroutineScope { // thisはCoroutineScope
    launch {
        delay(2000L)
        println("World 2")
    }
    launch {
        delay(1000L)
        println("World 1")
    }
    println("Hello")
}
```
`launch { ... }`ブロック内部の両方のコードは、並列に実行され、開始から1秒後に`World 1`が出力され、開始から2秒後に`World 2`が出力される。
# 明示的なジョブ
[launch](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html)コルーチンビルダーは、起動したコルーチンの取っ手であり、その完了を明示的に待つために使うことができる、[Job](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html)オブジェクトを返却する。例えば、子のコルーチンを待ち、その後"Done"文字列を出力することができる。
```Kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch { // 新しいコルーチンを起動し、そのJobへの参照を保持する。
        delay(1000L)
        println("World!")
    }
    println("Hello")
    job.join() // 子のコルーチンの完了を待つ
    println("Done")     
}
```
このコードは次の結果となる。
```
Hello
World!
Done
```
# コルーチンは軽量
コルーチンはJVMのスレッドよりも、リソースがよりかからない。リソースの限界に到達せずに、コルーチンを使って、スレッドを使うことを表現することができるとき、JVMの利用可能なメモリを使い果たすようコーディングをしてみる。例えば、次のコードは10万回、明示的なコルーチンを起動し、それぞれが5秒待ちピリオド('.')を表示するが、メモリの消費は非常に少ない。
```Kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    repeat(100_000) { // たくさんのコルーチンを起動する
        launch {
            delay(5000L)
            print(".")
        }
    }
}
```
もしスレッドを使う同じプログラムを書く(`runBlocking`を削除し、`launch`を`thread`に置換、`delay`を`Thread.sleep`に置換)する場合、おそらく過度にメモリを消費し、out-of-memoryエラーをスローするだろう。