サスペンド関数からコルーチンを起動し、現在のスコープを使用させるにはどうすればよいですか? (起動されたコルーチンも終了するまでスコープが終了しないように)
私は次のようなものを書きたいです-
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
go()
}
suspend fun go() {
launch {
println("go!")
}
}
ただし、これには「未解決の参照:起動」という構文エラーがあります。 launch
は、次のいずれかの方法で実行する必要があるようです–
GlobalScope.launch {
println("Go!")
}
または
runBlocking {
launch {
println("Go!")
}
}
または
withContext(Dispatchers.Default) {
launch {
println("Go!")
}
}
または
coroutineScope {
launch {
println("Go!")
}
}
これらの選択肢のどれも私が必要としていることをしません。コードは「スポーン」ではなく「ブロック」するか、スポーンしても親スコープ自体が終了する前に親スコープが完了するのを待機しません。
現在の親コルーチンスコープで「スポーン」(起動)する必要があります。その親スコープは、スポーンしたコルーチンが終了する前に、コルーチンが終了するのを待つ必要があります。
suspend fun
内の単純なlaunch
が有効であり、その親スコープを使用すると予想していました。
Kotlin 1.3
とcotlinx-coroutines-core:1.0.1
を使用しています。
関数go
をCoroutineScope
の拡張関数にする必要があります。
fun main() = runBlocking {
go()
go()
go()
println("End")
}
fun CoroutineScope.go() = launch {
println("go!")
}
これを読んで 記事 新しいcoroutineScope{}
を作成せずにsuspend
関数、他のコルーチンから始めるのがなぜ良い考えではないのかを理解してください。
規則は次のとおりです。並列コルーチンを開始する必要がある場合は、suspend
関数で他のsuspend
関数を呼び出し、新しいCoroutineScope
を作成します。その結果、新しく開始されたすべてのコルーチンが終了したときにのみ、コルーチンが返されます(構造化された並行性)。
一方、スコープを知らずに新しいコルーチンを開始する必要がある場合は、CoroutineScope
の拡張関数を作成しますが、それ自体はsuspendable
ではありません。これで、呼び出し元は使用するスコープを決定できます。
私はwith(CoroutineScope(coroutineContext)
という解決策を見つけたと思います。次の例はこれを示しています–
import kotlinx.coroutines.*
fun main() = runBlocking {
go()
go()
go()
println("End")
}
suspend fun go() {
// GlobalScope.launch { // spawns, but doesn't use parent scope
// runBlocking { // blocks
// withContext(Dispatchers.Default) { // blocks
// coroutineScope { // blocks
with(CoroutineScope(coroutineContext)) { // spawns and uses parent scope!
launch {
delay(2000L)
println("Go!")
}
}
}
ただし、ルネは上記のはるかに優れたソリューションを投稿しました。