LoginSignup
5
1

More than 3 years have passed since last update.

概要

kotlin coroutineについてキャッチアップしてみました。

参考資料:
Coroutine Basics
Cancellation and Timeouts
Composing Suspending Functions

基本系

  1. GlobalScope.launchでコルーチンを起動(1秒停止)
  2. println("Hello,")
  3. メインスレッドで2秒停止
  4. 1.の1秒停止が終わり、println("World!")
  5. 3.の2秒停止が終了
  6. アプリケーションが終了
main.kt
import kotlinx.coroutines.*

fun main() {
  GlobalScope.launch {
      delay(1000L) // コルーチンで1秒停止
      println("World!")
  }
  println("Hello,")
  Thread.sleep(2000L) // メインスレッドで2秒停止
}

ポイント

  • コルーチンはGlobalScope.launchで起動する
  • アプリケーションが終了すれば動作しない

Blocking(ブロッキング)

main.kt
fun main() {
    GlobalScope.launch {
        delay(1000L) // コルーチンで1秒停止 non-blocking
        println("World!")
    }
    println("Hello,")
    runBlocking {
        delay(2000L) // メインスレッドを2秒停止
    }
}

// 以下のようにすることでも同じ結果を得られる
fun main() = runBlocking {
    GlobalScope.launch {
        delay(1000L) // コルーチンで1秒停止 non-blocking
        println("World!")
    }
    println("Hello,")
    delay(2000L)
}

// runBlockingを用いることでコルーチンが終了するまで、アプリケーションの終了を待つことができる
fun main() = runBlocking {
    val job = GlobalScope.launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
    job.join()
}

// `GlobalScope.launch`ではなく`launch`を用いることでこのスコープ内でコルーチンを起動する
fun main() = runBlocking {
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
}

ポイント

  • runBlocking {...}を使用すると内部のコルーチンが終了するまでスレッドをブロック(停止)できる
  • GlobalScope.launchlaunchで起動するスコープを指定できる

coroutineScope(スコープ)

main.kt
fun main() = runBlocking {
    // 処理が完了するまでスレッドを停止する
    launch {
        delay(200L)
        println("Task from runBlocking")
    }

    coroutineScope {
        launch {
            delay(500L)
            println("Task from nested launch")
        }

        delay(100L)
        println("Task from coroutine scope")
    }

    println("Coroutine scope is over")
}

ポイント

  • runBlockingはブロック(停止)、coroutineScopeは一時停止
  • GlobalScope.launchlaunchで起動するスコープを指定できる

Cancel(キャンセル)

main.kt
fun main() = runBlocking {
    val job = launch {
        repeat(1000) { i ->
            println("job: I'm sleeping $i ...")
            delay(500L)
        }
    }
    delay(1300L)
    println("main: I'm tired of waiting!")
    job.cancel() // jobのキャンセル
    job.join() // job完了を待つ
    println("main: Now I can quit.")
}

fun main() = runBlocking {
    val job = launch {
      try {
        repeat(1000) { i ->
            println("job: I'm sleeping $i ...")
            delay(500L)
        }
      } finally {
        // 以下のコルーチンはキャンセルされないようにする
        withContext(NonCancellable) {
          println("job: I'm running finally")
          delay(1000L)
          println("job: And I've just delayed for 1 sec because I'm non-cancellable")
        }
      }
    }
    delay(1300L)
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // jobのキャンセル&完了を待つ
    println("main: Now I can quit.")
}

ポイント

  • cancelメソッドでjob(コルーチン)をキャンセルできる
  • joinでjob(コルーチン)の終了まで待つ
  • cancelAndJoinというcancelとjoinが合わさったメソッドもある
  • try {...} finally {...}を用いることでキャンセルされた際にfinallyブロックを通過する
  • withContext(NonCancellable) {...}を用いることでキャンセルされないようにできる

Timeout(タイムアウト)

main.kt
fun main() = runBlocking {
    withTimeout(1300L) {
        repeat(1000) { i ->
            println("I'm sleeping $i ...")
            delay(500L)
        }
    }
}

fun main() = runBlocking {
    val result = withTimeoutOrNull(1300L) {
    repeat(1000) { i ->
        println("I'm sleeping $i ...")
        delay(500L)
    }
    "Done" // will get cancelled before it produces this result
  }
  println("Result is $result")
}

ポイント

  • withTimeoutでjob(コルーチン)の制限時間を設定できます(タイムアウト時は例外をスローする)
  • withTimeoutOrNullではタイムアウト時にnullを返す

Suspend関数

main.kt
fun main() = runBlocking {
    launch { doWorld() }
    println("Hello,")
}

// this is your first suspending function
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}
main.kt
suspend fun doSomethingUsefulOne(): Int {
    delay(1000L)
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L)
    return 29
}

// The answer is 42
// Completed in 2017 ms
fun main() = runBlocking {
    val time = measureTimeMillis {
      val one = async { doSomethingUsefulOne() }
      val two = async { doSomethingUsefulTwo() }
      println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")
}

// The answer is 42
// Completed in 1017 ms
fun main() = runBlocking {
    val time = measureTimeMillis {
        val one = async { doSomethingUsefulOne() }
        val two = async { doSomethingUsefulTwo() }
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")
}

ポイント

  • Suspend修飾子を用いることでコルーチン内で使用できるdelay関数などを呼び出せる
  • suspend関数はコルーチンスコープないでしか呼び出せない
  • async/awaitキーワードを使用することによって非同期処理にできる

最後に

アルサーガパートナーズ Advent Calendar 2020 22日目は @miumiさんのターンです。:tada:

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1