gpt4 book ai didi

kotlin - 我们什么时候使用launch(Supervisor Job())?

转载 作者:行者123 更新时间:2023-12-02 12:41:06 25 4
gpt4 key购买 nike

我已经看到了将SupervisorJob传递给CoroutineScope的教程,以避免在子例程之一失败时所有协程作业被取消。
run3中,我认为将SupervisorJob传递给launch可以获得相同的结果,但显然没有。似乎允许协程在出现异常的情况下被重用(如果您从SupervisorJob中删除launch,则第二个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 from launch, second run2 call won't run the coroutine job



此行为的原因不是您要传递 SupervisorJob,而是要向其传递任何类型的 Job。尝试用 + SupervisorJob()替换 + Job(),第二次调用 run2()将执行协程。

主要区别在于,当您将显式作业传递给 launch时,它将成为已启动协程的父级作业,而不是 TestReuseCoroutineAfterException4中的主作业。因此,协程失败不会取消主作业,并且效果只限于一次调用。

不建议将工作直接传递给 launch,因为这会破坏结构化并发并创建您遇到的怪异语义。

关于kotlin - 我们什么时候使用launch(Supervisor Job())?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60026116/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com