Kotlin SupervisorJob 意外行为
Kotlin SupervisorJob unexpected behaviour
我有下一个代码:
import kotlin.coroutines.*;
import kotlinx.coroutines.*;
val job = SupervisorJob()
val handler = CoroutineExceptionHandler { _, e ->
println("Catch: $e")
}
val coroutineContext: CoroutineContext = Dispatchers.IO + job + handler
fun main() {
val job1 = GlobalScope.launch(coroutineContext) {
// supervisorScope {
launch {
println("Test0")
for (i in 0..5) {
println("Working! Iteration: $i")
delay(1000)
}
println("Test0 end")
}
launch {
println("Test1")
delay(2000)
throw IllegalAccessException()
}
launch {
println("Test2")
delay(3000)
println("Test2 end")
}
}
//}
runBlocking { job1.join() }
println("Test3")
}
如您所见,我使用 SupervisorJob
将其传递到 coroutineContext
到 GlobalScope.launch
以避免在任何地方发生错误时取消任何子项,并且错误必须是在 handler
中处理。文档接下来说 SupervisorJob
:
Creates a supervisor job object in an active state. Children of a supervisor job can fail independently of each other.
基于此,我期待这样的结果:
Test0
Working! Iteration: 0
Test1
Test2
Working! Iteration: 1
Catch: java.lang.IllegalAccessException
Working! Iteration: 2
Working! Iteration: 3
Test2 end
Working! Iteration: 4
Working! Iteration: 5
Test0 end
Test3
但我得到:
Test2
Test0
Working! Iteration: 0
Test1
Working! Iteration: 1
Catch: java.lang.IllegalAccessException
Test3
唯一有帮助的是在上面的代码中取消注释 supervisorScope
。
我做错了什么?例如,如何在 ViewModel
中全局设置预期行为,以避免在 supervisorScope
中包装每个 launch
?
提前致谢。
如 中所述,您无法更改已启动协程的作业类型。唯一受您控制的是它的 parent 工作。由于您首先启动一个顶级协程,然后作为其子级启动那些您不想影响其他协程的失败,最好的选择是您已经发现的,打开一个内部 supervisorScope
.
您不能指定关于给定协程在其子程序失败时的行为方式的全局策略。
感谢@MarkoTopolnik,解决方案对我有用:
val job = SupervisorJob()
val handler = CoroutineExceptionHandler { _, e ->
println("Catch: $e")
}
val coroutineContext: CoroutineContext = Dispatchers.IO + job + handler
val coroutineScope: CoroutineScope = CoroutineScope(coroutineContext)
fun main() {
val job1 = coroutineScope.launch {
println("Test0 start")
for (i in 0..5) {
println("Test0 iteration: $i")
delay(1000)
}
println("Test0 end")
}
val job2 = coroutineScope.launch {
println("Test4 start")
for (i in 0..5) {
println("Test4 iteration: $i")
delay(1000)
}
println("Test4 end")
}
val job3 = coroutineScope.launch {
println("Test1")
delay(2000)
throw IllegalAccessException()
}
val job4 = coroutineScope.launch {
println("Test2 start")
delay(4000)
println("Test2 end")
}
val jobs = mutableListOf(job1, job2, job3, job4)
runBlocking {
jobs.forEach { it.join() }
}
println("end")
}
结果:
Test0 start
Test0 iteration: 0
Test1
Test4 start
Test4 iteration: 0
Test2 start
Test0 iteration: 1
Test4 iteration: 1
Test0 iteration: 2
Test4 iteration: 2
Catch: java.lang.IllegalAccessException
Test4 iteration: 3
Test0 iteration: 3
Test2 end
Test4 iteration: 4
Test0 iteration: 4
Test4 iteration: 5
Test0 iteration: 5
Test4 end
Test0 end
end
我有下一个代码:
import kotlin.coroutines.*;
import kotlinx.coroutines.*;
val job = SupervisorJob()
val handler = CoroutineExceptionHandler { _, e ->
println("Catch: $e")
}
val coroutineContext: CoroutineContext = Dispatchers.IO + job + handler
fun main() {
val job1 = GlobalScope.launch(coroutineContext) {
// supervisorScope {
launch {
println("Test0")
for (i in 0..5) {
println("Working! Iteration: $i")
delay(1000)
}
println("Test0 end")
}
launch {
println("Test1")
delay(2000)
throw IllegalAccessException()
}
launch {
println("Test2")
delay(3000)
println("Test2 end")
}
}
//}
runBlocking { job1.join() }
println("Test3")
}
如您所见,我使用 SupervisorJob
将其传递到 coroutineContext
到 GlobalScope.launch
以避免在任何地方发生错误时取消任何子项,并且错误必须是在 handler
中处理。文档接下来说 SupervisorJob
:
Creates a supervisor job object in an active state. Children of a supervisor job can fail independently of each other.
基于此,我期待这样的结果:
Test0
Working! Iteration: 0
Test1
Test2
Working! Iteration: 1
Catch: java.lang.IllegalAccessException
Working! Iteration: 2
Working! Iteration: 3
Test2 end
Working! Iteration: 4
Working! Iteration: 5
Test0 end
Test3
但我得到:
Test2
Test0
Working! Iteration: 0
Test1
Working! Iteration: 1
Catch: java.lang.IllegalAccessException
Test3
唯一有帮助的是在上面的代码中取消注释 supervisorScope
。
我做错了什么?例如,如何在 ViewModel
中全局设置预期行为,以避免在 supervisorScope
中包装每个 launch
?
提前致谢。
如 supervisorScope
.
您不能指定关于给定协程在其子程序失败时的行为方式的全局策略。
感谢@MarkoTopolnik,解决方案对我有用:
val job = SupervisorJob()
val handler = CoroutineExceptionHandler { _, e ->
println("Catch: $e")
}
val coroutineContext: CoroutineContext = Dispatchers.IO + job + handler
val coroutineScope: CoroutineScope = CoroutineScope(coroutineContext)
fun main() {
val job1 = coroutineScope.launch {
println("Test0 start")
for (i in 0..5) {
println("Test0 iteration: $i")
delay(1000)
}
println("Test0 end")
}
val job2 = coroutineScope.launch {
println("Test4 start")
for (i in 0..5) {
println("Test4 iteration: $i")
delay(1000)
}
println("Test4 end")
}
val job3 = coroutineScope.launch {
println("Test1")
delay(2000)
throw IllegalAccessException()
}
val job4 = coroutineScope.launch {
println("Test2 start")
delay(4000)
println("Test2 end")
}
val jobs = mutableListOf(job1, job2, job3, job4)
runBlocking {
jobs.forEach { it.join() }
}
println("end")
}
结果:
Test0 start
Test0 iteration: 0
Test1
Test4 start
Test4 iteration: 0
Test2 start
Test0 iteration: 1
Test4 iteration: 1
Test0 iteration: 2
Test4 iteration: 2
Catch: java.lang.IllegalAccessException
Test4 iteration: 3
Test0 iteration: 3
Test2 end
Test4 iteration: 4
Test0 iteration: 4
Test4 iteration: 5
Test0 iteration: 5
Test4 end
Test0 end
end