英文:
Kotlin Coroutine SupervisorJob canceling behaviour
问题
我遇到了一个问题,不明白为什么任务2会在它是SupervisorJob的子任务且异常发生在另一个子任务时被取消。
文档中提到:
子任务的失败或取消不会导致SupervisorJob失败,也不会影响其他子任务,因此Supervisor可以实现自定义策略来处理其子任务的失败。
我是否漏掉了什么,或者有什么解决方法?感谢任何帮助。
英文:
code:
viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "task 1")
}) {
try {
Log.e("TAG", "task 1 start")
delay(3000)
Log.e("TAG", "task 1 finished")
} catch (ex: Exception) {
Log.e("TAG", "task 1 cancelled " + ex)
}
}
launch = viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 1" + myJob?.isCancelled)
}) {
myJob = SupervisorJob()
launch(myJob!! + CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 2 " + myJob?.isCancelled)
}) {
delay(300)
launch {
try {
Log.e("TAG", "task 2 start")
delay(5000)
Log.e("TAG", "task 2 finished")
} catch (ex: Exception) {
Log.e("TAG", "task 2 ex " + ex)
}
}
launch {
delay(2000)
throw Exception()
}
}
launch {
try {
Log.e("TAG", "task 3 start")
delay(3000)
Log.e("TAG", "task 3 finished")
} catch (ex: Exception) {
Log.e("TAG", "task 3 ex " + ex)
}
}
}
output:
2020-01-06 09:47:56.462 7159-7159/? E/TAG: task 1 start
2020-01-06 09:47:56.496 7159-7159/? E/TAG: task 3 start
2020-01-06 09:47:56.798 7159-7159/com.mvvm.template.debug E/TAG: task 2 start
2020-01-06 09:47:58.822 7159-7159/com.mvvm.template.debug E/TAG: task 2 ex kotlinx.coroutines.JobCancellationException: Parent job is Cancelling; job=StandaloneCoroutine{Cancelling}@81a8e39
2020-01-06 09:47:58.827 7159-7159/com.mvvm.template.debug E/TAG: handler 2 false
2020-01-06 09:47:59.464 7159-7159/com.mvvm.template.debug E/TAG: task 1 finished
2020-01-06 09:47:59.499 7159-7159/com.mvvm.template.debug E/TAG: task 3 finished
my issue:
I’m having an issue to understand why task 2 is canceled when it’s a child of SupervisorJob and the exception happened on an other child.
The documentation state:
A failure or cancellation of a child does not cause the supervisor job to fail and does not affect its other children, so a supervisor can implement a custom policy for handling failures of its children.
Am i missing something or what ? any help would be appreciated.
答案1
得分: 2
我不会为您提供代码部分的翻译。以下是您要求的文本翻译:
我不明白为什么任务2被取消,尽管它是
SupervisorJob
的子任务,异常却发生在另一个子任务中。
这不是直接回答这个问题,因为已经有一个被接受的答案解释了,父任务不是SupervisorJob
。
但为了实现使任务2独立于兄弟任务的预期行为:
您必须使用SupervisorJob
之一:
- 将
SupervisorJob
设置为您希望彼此独立的每个任务的父任务,在您的示例中:
val myJob = SupervisorJob()
viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 1" + myJob.isCancelled)
}) {
launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 2 " + myJob.isCancelled)
}) {
delay(300)
launch(myJob) { // <<< myJob is the parent
try {
Log.e("TAG", "task 2 start")
delay(5000)
Log.e("TAG", "task 2 finished")
} catch (ex: Exception) {
Log.e("TAG", "task 2 ex $ex")
}
}
launch(myJob) { // <<< myJob is the parent
delay(2000)
throw Exception()
}
}
//......
}
- 在
supervisorScope
挂起函数中添加子任务兄弟:
val launch = viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 1")
}) {
launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 2 ")
}) {
delay(300)
supervisorScope { /// <<< supervisor scope
launch {
try {
Log.e("TAG", "task 2 start")
delay(5000)
Log.e("TAG", "task 2 finished")
} catch (ex: Exception) {
Log.e("TAG", "task 2 ex $ex")
}
}
launch {
delay(2000)
throw Exception()
}
}
}
//....
}
现在,任务2将在不引发JobCancellationException
的情况下完成。
英文:
> I’m having an issue to understand why task 2 is canceled when it’s a child of SupervisorJob and the exception happened on an other child.
Here this is not a direct answer to the question as it's already answered well by the accepted answer as the parent is not the SupervisorJob
But in order to achieve the intended behavior of making task2 independent of the sibling jobs:
You've to use SupervisorJob
by either:
- Making the
SupervisorJob
the parent of each task that you want to be independent of one another:
In your example:
val myJob = SupervisorJob()
val launch = viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 1" + myJob.isCancelled)
}) {
launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 2 " + myJob.isCancelled)
}) {
delay(300)
launch(myJob) { // <<<<<<<<< myJob is the parent
try {
Log.e("TAG", "task 2 start")
delay(5000)
Log.e("TAG", "task 2 finished")
} catch (ex: Exception) {
Log.e("TAG", "task 2 ex $ex")
}
}
launch(myJob) { // <<<<<<<<< myJob is the parent
delay(2000)
throw Exception()
}
}
//......
}
- Add the child job siblings within a
supervisorScope
suspend function
val launch = viewModelScope.launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 1")
}) {
launch(CoroutineExceptionHandler { _, _ ->
Log.e("TAG", "handler 2 ")
}) {
delay(300)
supervisorScope { /// <<<< supervisor scope
launch {
try {
Log.e("TAG", "task 2 start")
delay(5000)
Log.e("TAG", "task 2 finished")
} catch (ex: Exception) {
Log.e("TAG", "task 2 ex $ex")
}
}
launch {
delay(2000)
throw Exception()
}
}
}
//....
}
Now task 2 will complete without raising JobCancellationException
答案2
得分: 1
你的答案就在日志中:
task 2 ex kotlinx.coroutines.JobCancellationException: 父作业正在取消;
作业=StandaloneCoroutine{Cancelling}@81a8e39
看看父作业:它是一个 StandaloneCoroutine
而不是你的 SupervisorJob
。
当你写
launch(myJob!!, handler) { ... }
myJob
成为所启动协程的 父作业,而协程本身始终与 launch
函数为其创建的作业相关联,类型为 StandaloneCoroutine
。在这个协程中,你启动了更多的协程,没有明确指定父作业,这意味着它们的父作业是协程的作业。它不是一个监督作业,因此会被取消。
英文:
Your answer is right there in the log:
task 2 ex kotlinx.coroutines.JobCancellationException: Parent job is Cancelling;
job=StandaloneCoroutine{Cancelling}@81a8e39
Look at the parent job: it's a StandaloneCoroutine
and not your SupervisorJob
.
When you write
launch(myJob!!, handler) { ... }
myJob
becomes the parent of the launched coroutine and the coroutine itself is always associated with a job the launch
function creates for it, of the type StandaloneCoroutine
. Inside this coroutine you launch more coroutines without explicitly specifying a parent, which means their parent is the coroutine's job. It is not a supervisor job and gets canceled.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论