Kotlin协程SupervisorJob取消行为

huangapple go评论132阅读模式
英文:

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之一:

  1. 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()
        }

    }

    //......
}
  1. 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:

  1. 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 { _, _ -&gt;
		Log.e(&quot;TAG&quot;, &quot;handler 1&quot; + myJob.isCancelled)
	}) {

		launch(CoroutineExceptionHandler { _, _ -&gt;
			Log.e(&quot;TAG&quot;, &quot;handler 2 &quot; + myJob.isCancelled)
		}) {

			delay(300)
			launch(myJob) { // &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; myJob is the parent
				try {
					Log.e(&quot;TAG&quot;, &quot;task 2 start&quot;)
					delay(5000)
					Log.e(&quot;TAG&quot;, &quot;task 2 finished&quot;)
				} catch (ex: Exception) {
					Log.e(&quot;TAG&quot;, &quot;task 2 ex $ex&quot;)
				}
			}

			launch(myJob) { // &lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; myJob is the parent
				delay(2000)
				throw Exception()
			}

		}

		//......
	}
  1. Add the child job siblings within a supervisorScope suspend function
val launch = viewModelScope.launch(CoroutineExceptionHandler { _, _ -&gt;
	Log.e(&quot;TAG&quot;, &quot;handler 1&quot;)
}) {
	launch(CoroutineExceptionHandler { _, _ -&gt;
		Log.e(&quot;TAG&quot;, &quot;handler 2 &quot;)
	}) {

		delay(300)

		supervisorScope { /// &lt;&lt;&lt;&lt; supervisor scope
			launch {
				try {
					Log.e(&quot;TAG&quot;, &quot;task 2 start&quot;)
					delay(5000)
					Log.e(&quot;TAG&quot;, &quot;task 2 finished&quot;)
				} catch (ex: Exception) {
					Log.e(&quot;TAG&quot;, &quot;task 2 ex $ex&quot;)
				}
			}

			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.

huangapple
  • 本文由 发表于 2020年1月6日 18:57:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/59610852.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定