Coroutineの基礎


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1907077/6664fe2d-9128-6e2e-b584-30153451ec12.png)

本記事はKotlin公式documentで学習を進める上で読んでまとめたnoteです。
詳細は公式から参照してください↓

https://kotlinlang.org/docs/coroutines-basics.html#coroutines-are-light-weight

# Coroutine

Kotlinは,言語として,他のさまざまな libraryが`coroutine`を利用できるようにするために,標準 libraryで最小限の低 levelAPIのみを提供します。Kotlinではasyncとawaitは keywordに含まれず,標準 libraryの一部ではありません。さらに,Kotlinの`suspend`機能の概念はfuturesやpromiseよりも安全で, errorが発生しにくい非同期処理の抽象化を提供します。

## 【Coroutines basics】

coroutineを理解する上で、`suspend(中断)`の概念について知っておく必要があります。coroutineでは、threadをblock(占有)する代わりに処理をsuspend(中断)します。 blockとsuspendの違いを見ていきましょう。

- `block`  
  blockは、threadを占有します。占有中は、threadで処理を進めることはできません。
  ```kotlin
  fun main(){
    println("Coroutine")
    Thread.sleep(3000L) // 3秒間、処理が停止。この間、処理は一切進まない。
    println("Hello")
  }
  ```

  ```kotlin
  Coroutine
  Hello
  ```

- `suspend`  
  suspendは、coroutineの処理をを中断し、threadを解放します。解放中は、他の処理にresourceを活用できます。
  ```kotlin
  fun main() = runBlocking {
      launch{
        delay(3000L) // 3秒間、処理を中断。threadが解放されるので先にHelloが表示される
        println("Coroutine")
      }
      println("Hello")
  }
  ```

  ```kotlin
  Hello
  Coroutine
  ```

前者の書き方では3秒の間、threadがblockされてしまうという問題があります。たとえば、applicationの使用中にthreadを占有してしまうとfreezeしてuserが画面を操作することができなくなります。 一方、後者の場合はsuspendするだけなので3秒の間、userは操作が可能です。

coroutineの`suspend(中断)`について理解を深めたところで実際に使っていきます。

### [Your first coroutine]

`coroutine`は<l>suspendable computationの instance</l>です。他の codeと同時に動作する code blockを実行するという意味で, `thread`と概念的に似ています。しかし, coroutineは<sl>**特定の threadに bindされません**</sl>。<sl>ある threadで実行をsuspendし,別の threadで再開することができます</sl>。

coroutineは軽量の threadと考えることができますが,実際の使用方法を threadとは大きく違う点がいくつかあります。

```kotlin
fun main() = runBlocking {  // this: coroutineScope
    launch { // 新しいcoroutineを起動
        delay(1000L) // non-blockingで1秒遅延
        println("World!") // 1秒後に表示
    }
    println("Hello") // 前のthreadが遅れている間に,main threadが実行される
}
```

```
Hello
World!
```

続いて,この codeが何をするかを分析してみましょう。

- `launch`は<l>coroutine builder</l>です。<sl>残りの codeと同時に新しいcoroutineを起動し,独立して機能し続けます</sl>。そのため,Helloが最初に表示されました。


- `delay`は<l>特殊なsuspend関数</l>です。coroutineを特定の時間だけ処理を中断し、基となるthreadを解放します。delayを記述するthreadはblockされますが基となるthreadはblockされません(non-blocking)。


- `runBlocking`は,<l>`fun main()`の非coroutineの世界と`runBlocking{...}`の block内のcoroutineを含む codeの橋渡しをするcoroutine</l>でもあります。たとえば,launchはcoroutineScopeのみで 宣言されます。

<sl>runBlockingの名前は,runBlocking {...}内のすべてのcoroutineが実行を完了するまで,それを実行する thread(この場合は main thread)が call中に blockされることを意味します</sl>。

> 高価な resourceである threadを blockすることは非効率的であり,望ましくないことが多いため実際の code内で`runBlocking`が使用されることはほとんどありません。

### [Structured concurrency]

coroutineは`Structured concurrencyの原則`に従います。つまり,**新しいcoroutineは,coroutineの存続期間を区切る特定のcoroutineScopeでのみ起動**できます。実際の applicationでは,多くのcoroutineが起動しますが,Structured concurrencyにより,それらが失われたり,leakしたりすることがなくなります。

### [Extract function refactoring]

`launch{...}`内の code blockを別の関数に抽出してみましょう。<sl>coroutineScope内の codeを抽出する場合,`suspend` 修飾子を使用します</sl>。

```kotlin
fun main() = runBlocking {  // this: coroutineScope 
    launch { doWorld() }
    println("Hello")
}

suspend fun doWorld() {
    delay(1000L)
    println("World!")
}
```

### [Scope builder]

さまざまな builderによって提供されるcoroutine scopeに加えて,`coroutineScope` builderを使用して独自の scopeを 宣言することができます。**coroutine scopeを作成し,起動されたすべての子が完了するまで完了しません。**

`runBlocking` builderと`coroutineScope` builderは,どちらも自分の体とそのすべての子が完了するのを待つため,一見似ているよう見えます。主な違いは次のようになります。

- `runBlocking`:  currentの threadを blockして待機します。


- `coroutineScope`: はsuspendし,基になる threadを他の用途に解放します。

この違いのため,runBlockingは通常の関数, coroutineScopeはsuspend関数として実行されます。

```kotlin
fun main() = runBlocking {
    doWorld()
}

suspend fun doWorld() = coroutineScope {  // this: coroutineScope
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello")
}
```

**sample code**

```kotlin
fun main() = runBlocking { // serial
    countToDown()
    println("fire!")
}

suspend fun countToDown() = coroutineScope { // concurrent
    launch {
        delay(2000L)
        println("1")
    }
    launch {
        delay(1000L)
        println("2")
    }
    println("3")
}
```

```
3
2
1
fire!
```

### [An explicit job]

`launch`coroutine builderは,起動されたcoroutineへの handleであり,その完了を明示的に待機するために使用できる`Job` objectを返します。たとえば,子coroutineの完了を待ってから,「Done」文字列を出力できます。

```kotlin
val job = launch { // 新しくcoroutineを起動し,そのjobへの参照を保持します
    delay(1000L)
    println("World!")
}
println("Hello")
job.join() // 子coroutineが完了するまで待ちます
println("Done") 
```

```
Hello
World!
Done
```

### [Coroutines are light-weight]

100Kのcoroutineを起動し,5秒後に各 coroutineが`.`を出力します。軽量なcoroutineでの実行は成功しますが,Threadで置き換えた場合,memory leakを引き起こします。

```kotlin
fun main() {
    launchCoroutine() // success!
    runThread() // java.lang.OutOfMemoryError
}

private fun launchCoroutines() = runBlocking {
    repeat(100_000) { // launch a lot of coroutines
        launch {
            delay(5000L)
            print(".")
        }
    }
}

private fun runThreads() {
    repeat(100_000) { // launch a lot of threads
        object:Thread() {
            override fun run() {
                sleep(5000L)
                print(".")
            }
        }.start()
    }
}
```

## まとめ

今回,学んだ内容で重要だと思う点をまとめてみました。

- 軽量で安全に扱える非同期処理の１つ
- threadはblockせずにsuspend(中断)を使用する
- 特定のthreadにbindされないので,別のthreadで再開できる
- coroutineScope内で起動されたcoroutineは,すべての子が完了するまで完了しない