web-dev-qa-db-ja.com

コルーチン:runBlockingとcoroutineScope

私は コルーチンの基本 を読んでそれを理解し、学習しようとしていました。

このコードには一部があります:

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は、最後の行に到達するまで到達しないため、ブロッキングのように見えます。

誰でもここで私を啓発できますか?

前もって感謝します。

24

ここでcoroutineScopeとrunBlockingの違いがわかりませんか? coroutineScopeは、最後の行に到達するまで到達しないため、ブロッキングのように見えます。

ブロック内のコードの観点から、あなたの理解は正しいです。 runBlockingcoroutineScopeの違いは下位レベルで発生します。コルーチンがブロックされている間にスレッドに何が起きているのでしょうか?

  • runBlockingsuspend funではありません。これを呼び出したスレッドは、コルーチンが完了するまで内部に残ります。

  • coroutineScopesuspend funです。コルーチンが中断されると、coroutineScope関数も中断されます。これにより、コルーチンを作成した最上位の関数non-suspending関数が同じスレッドで実行を継続できます。スレッドはcoroutineScopeブロックを「エスケープ」し、他の作業を行う準備ができています。

具体的な例では、coroutineScopeが一時停止すると、制御はrunBlocking内の実装コードに戻ります。このコードは、その中で開始したすべてのコルーチンを駆動するイベントループです。あなたの場合、遅延後に実行するようにスケジュールされたコルーチンがいくつかあります。時間が来ると、適切なコルーチンが再開されます。コルーチンはしばらくの間実行され、一時停止し、その後runBlocking内で再び制御されます。


上記では概念的な類似性について説明しましたが、runBlockingcoroutineScopeからの完全に異なるツールであることも示す必要があります。

  • runBlockingは低レベルの構成体であり、フレームワークコードまたはあなたのような自己完結型の例でのみ使用されます。既存のスレッドをイベントループに変換し、再開するコルーチンをイベントループのキューにポストするDispatcherを使用してコルーチンを作成します。

  • coroutineScopeはユーザー向けの構造体であり、その中で並列分解されるタスクの境界を描くために使用されます。これを使用して、内部で発生するすべてのasync作業を便利に待ち、最終結果を取得し、すべての障害を1か所で処理します。

23
Marko Topolnik

runBlocking just blocks内部コルーチンが完了するまでの現在のスレッド。ここで、runBlockingを実行するスレッドはブロックされるまでcoroutineScopeからのコルーチンが終了します。

最初のlaunchは、スレッドがrunBlockingの後に来る命令を実行することを許可しませんが、このlaunchブロックの直後に来る命令に進むことを許可します-理由はTask from coroutine scopeTask from runBlockingより前に出力されます。

ただし、coroutineScopeのコンテキストでネストされたrunBlockingは、coroutineScopeがスレッドをブロックするため、このrunBlockingブロックの後に来る命令をスレッドが実行することを許可しません。 coroutineScopeのコルーチンが完全に終了するまで。そして、それがCoroutine scope is overが常にTask from nested launchの後に来る理由です。

5
Andrey Ilyunin

この素晴らしい記事から 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の実行全体が終了するまでスレッドを強制的にブロックしていました。

0
onmyway133