Kotlin Coroutine 整理1編

# はじめに
Kotlinを学習する中で、新しく学んだことを整理いたします。

## ルーチンとコルーチンの違い
- ルーチンは一度開始すると、終了するまで止まりません。
- ルーチンは一度終了すると、ルーチン内の情報が消えてしまいます。

- コルーチンは中断され、再開することが可能です。
- 中断されても、ルーチン内の情報は消えません。

~~~ kotlin

package coroutine

import kotlinx.coroutines.*

suspend fun newRoutine(){ // 他のsuspend関数を呼び出すことができます。
    val num1 = 1
    val num2 = 2
    yield() // スレッドを譲ります。中断と再開。
    printWithThread("${num1 + num2}") // ルーチンが中断された後、メモリ上に保存されて再開されます。
}

fun main(): Unit = runBlocking { // コルーチンの世界へ接続します。
    printWithThread("START")
    launch { // 戻り値のないコルーチンを作成します。
        newRoutine() // すぐには実行されません。
    }
    yield() // newRoutineで譲渡され、ENDが呼び出されます。
    printWithThread("END")
}

fun printWithThread(str: Any){
    println("[${Thread.currentThread().name}] $str")
}

~~~

## 結果

`runBlocking`を実行して1番目のコルーチンを実行し、`launch`で2番目のコルーチンを実行します。しかし、`yield`の宣言により途中でメモリに保存された状態で再開されます。

~~~
[main @coroutine#1] START
[main @coroutine#1] END
[main @coroutine#2] 3
~~~

## プロセス、スレッド、そしてコルーチン

スレッドはプロセスに従属しています。スレッドがプロセスを変更することはできません。
コルーチンのコードが実行されるためには、スレッドが必要です。
中断後に再開される際には、別のスレッドに割り当てられることがあります。

コルーチンのコードはどのスレッドでも実行できます。

プロセスは独立したメモリを持ち、コンテキストスイッチング時にはすべてのデータが変更されます。

スレッドはヒープエリアを共有し、スタックエリアだけが置き換えられます。

コルーチンが同じスレッドで実行される場合、コンテキストスイッチングが異なります。
メモリの置き換えは発生しません。 -> **並行性**

コルーチンは自らの位置を譲ることができます。**非プリエンプティブ**

## runBlocking

新しいコルーチンを作成し、ルーチンとコルーチンを接続します。
スレッドをブロッキングします。

~~~ kotlin

fun main() { // コルーチンの世界へ接続します。
    runBlocking {
        printWithThread("START")
        launch {
            delay(2_000L) // yield
            printWithThread("LAUNCH END")
        }
    }
    printWithThread("END")
}

~~~

## 結果

~~~
[main @coroutine#1] START
[main @coroutine#2] LAUNCH END
[main] END
~~~

## launch

戻り値のないコードを実行します。
`Job`オブジェクトを返すことができます。

### start

~~~ kotlin
fun main(): Unit = runBlocking { // コルーチンの世界へ接続します。
    val job = launch(start = CoroutineStart.LAZY) {
        printWithThread("Hello launch")
    }

    delay(1000)
    job.start()
}
~~~

### cancel

~~~ kotlin
fun main(): Unit = runBlocking { // コルーチンの世界へ接続します。
    val job = launch {
        (1..5).forEach{
            printWithThread(it)
            delay(300)
        }
    }

    delay(1000)
    job.cancel()
}
~~~

~~~
[main @coroutine#2] 1
[main @coroutine#2] 2
[main @coroutine#2] 3
[main @coroutine#2] 4
~~~

### join

`await`に似ています。

~~~ kotlin
fun main(): Unit = runBlocking { // コルーチンの世界へ接続します。
    val job1 = launch {
        delay(1000)
        printWithThread("job 1")
    }
    job1.join()
    val job2 = launch {
        delay(1000)
        printWithThread("job 2")
    }
    job2.start()
}
~~~

### async

結果を返すことができます。`Promise`のようなものです。
複数のAPIを一度に呼び出すことができます。

~~~ kotlin

fun main(): Unit = runBlocking { // コルーチンの世界へ接続します。
    val job1 = async {
        3 + 5
    }
    val eight = job1.await()
    print(eight)
}

~~~

~~~ kotlin

fun main(): Unit = runBlocking { // コルーチンの世界へ接続します。
    val time = measureTimeMillis {
        val job2 = async { apiCall2() }
        val job1 = async { apiCall1() }
        printWithThread(job1.await() + job2.await())
    }
    printWithThread("所要時間: $time ms")
}

/*
fun main(): Unit = runBlocking { // コルーチンの世界へ接続します。
    val job1 = async { apiCall1() }
    val job2 = async { apiCall2(job1.await()) }
    
}

suspend fun apiCall1(): Int{
    delay(1000L)
    return 1
}

suspend fun apiCall2(num : Int): Int{
    delay(1000L)
    return 2 + num
}
*/

suspend fun apiCall1(): Int{
    delay(1000L)
    return 1
}

suspend fun apiCall2(): Int{
    delay(1000L)
    return 2
}

/*
[main @coroutine#1] 3
[main @coroutine#1] 所要時間: 1065 ms
*/
~~~

## Coroutine.LAZY

`CoroutineStart.LAZY`は、実際にコルーチンの実行が完了するまで待機するため、時間がかかります。

~~~ kotlin
    val job2 = async(start= CoroutineStart.LAZY) { apiCall2() }
~~~