私は コルーチンの基本 を読んでそれを理解し、学習しようとしていました。
このコードには一部があります:
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking")
}
coroutineScope { // Creates a new coroutine scope
launch {
delay(900L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before nested launch
}
println("Coroutine scope is over") // This line is not printed until nested launch completes
}
出力は次のようになります。
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
私の質問は、なぜこの行:
println("Coroutine scope is over") // This line is not printed until nested launch completes
常に最後に呼び出されますか?
次の理由から呼び出されるべきではありません:
coroutineScope { // Creates a new coroutine scope
....
}
中断されていますか?
メモもあります:
RunBlockingとcoroutineScopeの主な違いは、すべての子が完了するのを待っている間、後者が現在のスレッドをブロックしないことです。
ここでcoroutineScopeとrunBlockingの違いを理解できませんか? coroutineScopeは、最後の行に到達するまで到達しないため、ブロッキングのように見えます。
誰でもここで私を啓発できますか?
前もって感謝します。
ここでcoroutineScopeとrunBlockingの違いがわかりませんか? coroutineScopeは、最後の行に到達するまで到達しないため、ブロッキングのように見えます。
ブロック内のコードの観点から、あなたの理解は正しいです。 runBlocking
とcoroutineScope
の違いは下位レベルで発生します。コルーチンがブロックされている間にスレッドに何が起きているのでしょうか?
runBlocking
はsuspend fun
ではありません。これを呼び出したスレッドは、コルーチンが完了するまで内部に残ります。
coroutineScope
はsuspend fun
です。コルーチンが中断されると、coroutineScope
関数も中断されます。これにより、コルーチンを作成した最上位の関数non-suspending関数が同じスレッドで実行を継続できます。スレッドはcoroutineScope
ブロックを「エスケープ」し、他の作業を行う準備ができています。
具体的な例では、coroutineScope
が一時停止すると、制御はrunBlocking
内の実装コードに戻ります。このコードは、その中で開始したすべてのコルーチンを駆動するイベントループです。あなたの場合、遅延後に実行するようにスケジュールされたコルーチンがいくつかあります。時間が来ると、適切なコルーチンが再開されます。コルーチンはしばらくの間実行され、一時停止し、その後runBlocking
内で再び制御されます。
上記では概念的な類似性について説明しましたが、runBlocking
がcoroutineScope
からの完全に異なるツールであることも示す必要があります。
runBlocking
は低レベルの構成体であり、フレームワークコードまたはあなたのような自己完結型の例でのみ使用されます。既存のスレッドをイベントループに変換し、再開するコルーチンをイベントループのキューにポストするDispatcher
を使用してコルーチンを作成します。
coroutineScope
はユーザー向けの構造体であり、その中で並列分解されるタスクの境界を描くために使用されます。これを使用して、内部で発生するすべてのasync
作業を便利に待ち、最終結果を取得し、すべての障害を1か所で処理します。
runBlocking
just blocks内部コルーチンが完了するまでの現在のスレッド。ここで、runBlocking
を実行するスレッドはブロックされるまでcoroutineScope
からのコルーチンが終了します。
最初のlaunch
は、スレッドがrunBlocking
の後に来る命令を実行することを許可しませんが、このlaunch
ブロックの直後に来る命令に進むことを許可します-理由はTask from coroutine scope
はTask from runBlocking
より前に出力されます。
ただし、coroutineScope
のコンテキストでネストされたrunBlocking
は、coroutineScope
がスレッドをブロックするため、このrunBlocking
ブロックの後に来る命令をスレッドが実行することを許可しません。 coroutineScope
のコルーチンが完全に終了するまで。そして、それがCoroutine scope is over
が常にTask from nested launch
の後に来る理由です。
この素晴らしい記事から https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}
RunBlockingでは、構造化された同時実行性を使用していなかったため、fの呼び出しが失敗し、他のすべての実行がそのまま続行されます。また、残りのコードでNiceをプレイしていませんでした。 runBlockingを使用することにより、呼び出し側に実行方法を決定させるのではなく、pmapの実行全体が終了するまでスレッドを強制的にブロックしていました。