我看过一些教程,将 SupervisorJob
传递给 CoroutineScope
以避免所有协程作业在其子进程之一失败时被取消。
在run3
中,我认为将SupervisorJob
传递给launch
可以得到相同的结果,但显然事实并非如此。如果出现异常,它似乎允许重用协程(如果从 launch
中删除 SupervisorJob
,则第二个 run2
调用将不会运行协程作业),但它的行为不像 supervisorScope
,后者的其他子作业可以继续(在示例中是第一个 test1.run
调用)。我想知道在什么场景下我们可以使用这种方式?因为将其传递给 launch
构造函数看起来是合法的。
package coroutine.exceptions
import kotlinx.coroutines.*
fun log(msg: String) = println("$msg (${Thread.currentThread().name})")
val logExceptionHandler = CoroutineExceptionHandler { _, e ->
log(e.localizedMessage)
}
fun main() = runBlocking {
TestReuseCoroutineAfterException4("test1").run {
run1(true)
delay(100)
println()
run1(false)
delay(100)
}
println("================================================================")
TestReuseCoroutineAfterException4("test2").run {
run2(true)
delay(100)
println()
run2(false)
delay(100)
}
println("================================================================")
TestReuseCoroutineAfterException4("test3").run {
run3(true)
delay(100)
println()
run3(false)
delay(100)
println()
}
log("finished")
}
class TestReuseCoroutineAfterException4(
private val testName: String
) : CoroutineScope by CoroutineScope(CoroutineName(testName)) {
// by passing a Job, we can let the exception propagate to this coroutine scope instead of the
// root one, which allows us to reuse the root scope.
fun run1(throwException: Boolean) = launch(logExceptionHandler + Job()) {
val logPrefix = "$testName.run1:"
coroutineScope {
launch {
launch {
if (throwException)
throw RuntimeException("$logPrefix throw exception")
else
log("$logPrefix done (job#1-1)")
}.join()
launch {
log("$logPrefix done (job#1-2)")
}.join()
log("$logPrefix done (job#1)")
}.join()
launch {
log("$logPrefix done (job#2)")
}.join()
}
}
suspend fun run2(throwException: Boolean) {
val logPrefix = "$testName.run2:"
supervisorScope {
launch(logExceptionHandler) {
launch {
if (throwException)
throw Exception("$logPrefix throw exception")
else
log("$logPrefix done (job#1-1)")
}.join()
launch {
log("$logPrefix done (job#1-2)")
}.join()
log("$logPrefix done (job#1)")
}.join()
// this will be run.
launch {
log("$logPrefix done (job#2)")
}.join()
}
}
fun run3(throwException: Boolean) {
val logPrefix = "$testName.run3:"
launch(logExceptionHandler + SupervisorJob()) {
launch {
launch {
if (throwException)
throw Exception("$logPrefix throw exception")
else
log("$logPrefix done (job#1-1)")
}.join()
launch {
log("$logPrefix done (job#1-2)")
}.join()
log("$logPrefix done (job#1)")
}.join()
// this will still be run.
launch {
log("$logPrefix done (job#2)")
}.join()
}
}
}
输出
test1.run1: throw exception (DefaultDispatcher-worker-2 @test1#2)
test1.run1: done (job#1-1) (DefaultDispatcher-worker-2 @test1#7)
test1.run1: done (job#1-2) (DefaultDispatcher-worker-2 @test1#8)
test1.run1: done (job#1) (DefaultDispatcher-worker-2 @test1#6)
test1.run1: done (job#2) (DefaultDispatcher-worker-2 @test1#9)
================================================================
test2.run2: throw exception (main @coroutine#10)
test2.run2: done (job#2) (main @coroutine#12)
test2.run2: done (job#1-1) (main @coroutine#14)
test2.run2: done (job#1-2) (main @coroutine#15)
test2.run2: done (job#1) (main @coroutine#13)
test2.run2: done (job#2) (main @coroutine#16)
================================================================
test3.run3: throw exception (DefaultDispatcher-worker-2 @test3#18)
test3.run3: done (job#1-1) (DefaultDispatcher-worker-4 @test3#22)
test3.run3: done (job#1-2) (DefaultDispatcher-worker-4 @test3#23)
test3.run3: done (job#1) (DefaultDispatcher-worker-4 @test3#21)
test3.run3: done (job#2) (DefaultDispatcher-worker-4 @test3#24)
finished (main @coroutine#1)
Process finished with exit code 0
最佳答案
if you remove the
SupervisorJob
fromlaunch
, secondrun2
call won't run the coroutine job
出现此行为的原因不是您正在传递 SupervisorJob
,而是您正在向其传递任何 类型的 Job
。尝试将 + SupervisorJob()
替换为 + Job()
,第二次调用 run2()
将执行协程。
主要区别在于,当您将显式作业传递给 launch
时,它会成为已启动协程的父作业,而不是 TestReuseCoroutineAfterException4
中的主作业。因此,协程失败不会取消主作业,并且效果仅限于单个调用。
不鼓励将作业直接传递给 launch
,因为它会破坏结构化并发并创建您所经历的奇怪语义。
关于kotlin - 我们什么时候使用launch(SupervisorJob())?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60026116/