概要
kotlin coroutineについてキャッチアップしてみました。
参考資料:
Coroutine Basics
Cancellation and Timeouts
Composing Suspending Functions
基本系
-
GlobalScope.launch
でコルーチンを起動(1秒停止) - println("Hello,")
- メインスレッドで2秒停止
- 1.の1秒停止が終わり、println("World!")
- 3.の2秒停止が終了
- アプリケーションが終了
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.launch
とlaunch
で起動するスコープを指定できる
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.launch
とlaunch
で起動するスコープを指定できる
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さんのターンです。