web-dev-qa-db-ja.com

kotlinコルーチン、coroutineScopeとwithContextの違いは何ですか

_withContext
suspend fun <T> withContext(
    context: CoroutineContext, 
    block: suspend CoroutineScope.() -> T
): T (source)
Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns the result.
_
_suspend fun <R> coroutineScope(
    block: suspend CoroutineScope.() -> R
): R (source)
Creates a CoroutineScope and calls the specified suspend block with this scope. The provided scope inherits its coroutineContext from the outer scope, but overrides the context’s Job.
_

withContextはCoroutineContextを受け取り、そのすべての子が完了した後はどちらもcompleteのようです。

withContextcoroutineScopeのどちらを優先するべきですか。

例えば:

_suspend fun processAllPages() = withContext(Dispatchers.IO) { 
    // withContext waits for all children coroutines 
    launch { processPages(urls, collection) }
    launch { processPages(urls, collection2) }
    launch { processPages(urls, collection3) }
}
_

かもしれない

_suspend fun processAllPages() = coroutineScope { 
    // coroutineScope waits for all children coroutines 
    launch { processPages(urls, collection) }
    launch { processPages(urls, collection2) }
    launch { processPages(urls, collection3) }
}
_

両方ともprocessAllPages()は同じことをしていますか?


更新: で議論を参照してくださいなぜwithContextが子コルーチンの完了を待つのか

10
lannyf

正式には、coroutineScopewithContextの特殊なケースであり、現在のコンテキストで渡して、コンテキストの切り替えを回避します。概略的に言えば、

coroutineScope ≡ withContext(this.coroutineContext)

コンテキストの切り替えはwithContextのいくつかの機能の1つにすぎないため、これは正当な使用例です。 withContextは、ブロック内で開始したすべてのコルーチンが完了するまで待機します。それらのいずれかが失敗すると、他のすべてのコルーチンが自動的にキャンセルされ、ブロック全体で例外がスローされますが、呼び出し元のコルーチンは自動的にキャンセルされません。

コンテキストを切り替えることなくこれらの機能が必要な場合は、意図をより明確に伝えるため、常にcoroutineScopeを優先する必要があります。

coroutineScopeは、いくつかのサブコルーチンのスコープ付きライフサイクルに関するものです。タスクをいくつかの同時サブタスクに分解するために使用されます。コンテキストを変更することはできないため、現在のコンテキストからDispatcherを継承します。通常、各サブコルーチンは、必要に応じて異なるDispatcherを指定します。

withContextは通常、サブコルーチンの開始には使用されませんが、現在のコルーチンのコンテキストを一時的に切り替えるために使用されます。コードブロックが完了するとすぐに完了します(バージョン1.3.2以降、これは実際にはまだドキュメントに記載されています)。その主な使用例は、長い操作をイベントループスレッド(メインGUIスレッドなど)から、独自のスレッドプールを使用するDispatcherにオフロードすることです。別の使用例は、コルーチンがキャンセル要求に反応しない「クリティカルセクション」を定義することです。

6
Marko Topolnik