Kotlin Coroutineチュートリアル

# そろそろやってみるべき？

まだexperimentalですが、そろそろ正式版になるようですし(Kotlin 1.3予定)、[production ready](https://blog.jetbrains.com/kotlin/2017/11/kotlinconf-keynote-recap/)らしいし、身につけておきたい技術になってきました。

こちらにコルーチンのチュートリアルがあるのを発見したのでやってみながら翻訳しました
https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html


async await自体はさまざまな言語にもありますし、そういう意味でも、ちゃんと理解しておくべきかなと思いました。
コルーチンはKotlin 1.1で非同期処理の新しい方法として導入されました。
見てみて思ったのですが、すごく基本的で初歩的なのですが、割と導入には分かりやすくて良いと思いました。
これで心置きなくk-kagurazakaさんの[入門Kotlin coroutines](https://qiita.com/k-kagurazaka@github/items/8595ca60a5c8d31bbe37) に行けそう？な気がします。

# プロジェクト作成
IntelliJでKotlinのプロジェクトを作ります。
ここからIntelliJをインストールしましょう。Communityで大丈夫です。簡単です。
https://www.jetbrains.com/idea/download/?fromIDE=#section=mac

プロジェクト作成時にKotlinで作るとコルーチンを有効にする方法が分からなかったので、Gradleで作りましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/4cf7abfb-aa08-e1b2-298b-89d50085ee6a.png)


```gradle
apply plugin: 'kotlin'

kotlin {
    experimental {
        coroutines 'enable'
    }
}
```

現時点での最新版は0.22.3みたいなのでそれを入れます。(ここでみれそう
 https://github.com/Kotlin/kotlinx.coroutines/releases )

```
dependencies {
    ...
    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.22.3"
}
```

```
repositories {
    jcenter()
}
```


以下の文章はほぼ翻訳になります


# 最初のコルーチン

コルーチンは簡単なスレッドとして考えることが出来ます。スレッドのようにコルーチンは並列で走ることができ、それぞれが待って連携することができます。一番の違いはコルーチンはとてもパフォーマンス的に安価(cheap)でほとんどただで使うことが出来ます。1000個作ることだって出来てパフォーマンス的にはほとんど影響がありません。一方本当のスレッドでは開始して保持することは高価です。1000スレッドだと重大な問題になる事があります。

`launch {} `を使って、コルーチンを始めてみよう

```kotlin
launch {
    ...
}
```

デフォルトではコルーチンはスレッドプールを利用して動作します。コルーチンのプログラムでもスレッドはまだ存在しますが1つのスレッドがたくさんのコルーチンを実行することができます。そのためたくさんのスレッドが必要ありません。
`launch`を使ったプログラムを見てみましょう。

```kotlin
import kotlinx.coroutines.experimental.*

fun main(args: Array<String>) {
    println("Start")

    launch {
        delay(1000)
        println("Hello")
    }
    Thread.sleep(2000)
    println("Stop")
}
```

1秒待ってから"Hello"が表示されます。
今回、`delay()`を使いました。それは`Thread.sleep()`のようです。しかしもっと良いです。`delay()`はスレッドをブロックしません。ただコルーチンだけを一時停止します。スレッドはコルーチンが待っている間はプールに戻っていて、待ち終わったらコルーチンはプールの中の自由なスレッドによって復帰します。

メインスレッド(main()関数を実行している)はコルーチンが終わるのを待つ必要がある、そうでなければHelloが出力されるまでにプログラムが終了してします。

Exercise: sleep()を消して結果を見よう
→ StartとStopだけ出力される。

もし`delay()`をmain()関数の中で直接呼び出すと、エラーになります。

```
Suspend functions are only allowed to be called from a coroutine or another suspend function
```

なぜならコルーチンの中でないためです。コルーチンを開始して、コルーチンが終了するのを待つ`runBlocking {} `でラップすることによってdelayを使うことができます。

```kotlin
runBlocking {
    delay(2000)
}
```

最初にStartをprintして`launch {}`によってコルーチンを開始、そしてもう一つ`runBlocking {}`が行われ、それが終わるまでブロックされ、そして`Stop`が出力されます。それまでの間に最初のコルーチンが完了し、`Hello`が出力されます。これは話しているようにスレッドのようです。


# たくさんのコルーチンを動かしてみよう
コルーチンはスレッドより安価なのを確かめてみましょう100万のコルーチンを動かしたらどうなるでしょうか？まず最初に100万のスレッドを動かしてみましょう。

```kotlin
val c = AtomicInteger()

for (i in 1..1_000_000)
    thread(start = true) {
        c.addAndGet(i)
    }

println(c.get())
```

これは1'000'000のスレッドが1つのカウンタを上げていっています。このプログラムが終わる前に私の忍耐が切れました。(おそらく1分以上)

これをコルーチンでやってみましょう。

```kotlin
val c = AtomicInteger()

for (i in 1..1_000_000)
    launch {
        c.addAndGet(i)
    }

println(c.get())
```

私の環境では1秒以内にこれは終わるが気まぐれな数字が返ってきます。なぜならいくつかのコルーチンはmain()関数のprintが行われる前に終わらなかったからです。これを修正してみましょう。

スレッドと同様の同期手段を利用できます。(この場合CountDownLatchが思い浮かぶ。)しかしもっと安全でもっときれいな方法でやってみよう。

# Async: コルーチンから値を返す
コルーチンを開始するもう一つの方法が`async {}`です。`async {}`は`launch {}`のようですが`Deferred<T> `型を返却します。`Deferred<T>`は`await()`関数を持っていて、コルーチンの結果を返します。`Deferred<T>`は基本的な[future](https://en.wikipedia.org/wiki/Futures_and_promises)です(JDKのfutureもサポートされていますが、現在は`Deferred`に限定しています。)

Deferredオブジェクトを保持して、もう一度100万のコルーチンを作ってみましょう！これからはAtomicCounterは必要なく、コルーチンでただ追加される値を返すだけで良いです。

```kotlin
val deferred = (1..1_000_000).map { n ->
    async {
        n
    }
}
```

それらはすでに始まっているので私たちはその結果を集める必要があります。

```kotlin
val sum = deferred.sumBy { it.await() }
```

私たちはただそれぞれのコルーチンを取り、結果をawaitして待って、標準ライブラリの`sumBy() `で合計しているだけです。ただコンパイラは怒ります。

```
Suspend functions are only allowed to be called from a coroutine or another suspend function
```
`await()`はコルーチンの外で呼ぶことができません。なぜなら計算結果が出るまで一時停止する必要があり、コルーチンだけがブロックせずに一時停止することができるためです。なので、コルーチンの中に書いてみましょう。

```kotlin
runBlocking {
    val sum = deferred.sumBy { it.await() }
    println("Sum: $sum")
}
```

この結果`1784293664`が出力されます。なぜなら全てのコルーチンが終わったためです。

そしてこれが並列で動いていることを確認してみましょう。もし1秒の`delay()`をそれぞれの`async`に追加したとして、1'000'000秒(11.5日)かからないです。

```kotlin
val deferred = (1..1_000_000).map { n ->
    async {
        delay(1000)
        n
    }
}
```

これはだいたい10秒かかります。つまりコルーチンは並列で動いているのです。

# コルーチンを中断する
さて、1秒待ってから値を返すというのをメソッドに分割したいとします。

```kotlin
fun workload(n: Int): Int {
    delay(1000)
    return n
}
```

優しいエラーが表示されます。

```
Suspend functions are only allowed to be called from a coroutine or another suspend function
```

(suspendの関数だけがコルーチンや他のsuspendの関数から呼び出すことができます。)

この意味をちょっと深掘りしてみましょう。コルーチンの一番のメリットはスレッドをブロックしないことです。コンパイラはこれを実現するために特別なコードを生成します。そのため、私たちはコード内で明白に一時停止することを印としてつける必要がある。私たちはsuspend修飾子をそれのために使います。

```kotlin
suspend fun workload(n: Int): Int {
    delay(1000)
    return n
}
```

これでコンパイラはそれが一時停止することを知り、私たちは`workload() `をコルーチンから呼び出すことが出来るようになりました。そして以下のように呼び出します。

```kotlin
async {
    workload(n)
}
```

`workload()`関数はコルーチン(か他のsuspend関数)から呼び出すことができるが、コルーチンの外からは呼び出すことができません。`delay()`と`await()`はそれ自体が`suspend`として宣言されています。そしてそれが`runBlocking {}`, `launch {}` または`async {}`の中に書かなければいけなかった理由です。