【问题标题】:How to join a Kotlin SupervisorJob如何加入 Kotlin 主管职位
【发布时间】:2019-05-23 18:58:51
【问题描述】:

我正在尝试处理数据对象树。每个树叶都应该通过使用协程的函数进行处理。整个过程应该使用一个固定大小的线程池来完成。

所以我想出了这个:

val node = an instance of WorkspaceEntry (tree structure)
val localDispatcher = newFixedThreadPoolContext(16)

fun main() {
    val job = SupervisorJob()
    val scope = CoroutineScope(localDispatcher + job)
    handleEntry(node, scope)

    runBlocking {
        job.join()
    }
}

handleEntry 方法在主管中为每个树叶递归地启动一个子作业。

主管的子作业都成功完成,但连接永远不会返回。我理解错了吗?

编辑:HandleEntry 函数

private fun handleEntry(workspaceEntry: WorkspaceEntry, scope: CoroutineScope) {
    if (workspaceEntry is FileEntry) {
        scope.launch {
            FileTypeRegistry.processFile(workspaceEntry.fileBlob)
        }
    } else {
        workspaceEntry.children.forEach { child -> handleEntry(child, scope) }
    }
}

【问题讨论】:

  • 添加handleEntry函数的代码。
  • “加入永不返回”是指线程被阻塞并且应用程序被挂起吗? main 函数没有完成?
  • 是的。它将无限期地等待作业完成。我检查了:子作业确实完成并被销毁,直到主管没有任何孩子。但作业本身永远不会进入完整状态。
  • 好像如果你 cancel SupervisorJob 它取消了它的所有子节点,你可以等待它和它的子节点完成,然后使用 join。但是当然,您在每个子作业中都会得到一个可能不需要的CancellationException,如果它调用某些suspend 函数,则会导致停止执行。

标签: kotlin kotlin-coroutines jobs


【解决方案1】:

似乎用于创建CoroutineContext(在您的情况下为SupervisorJob)的Job 不适用于等待子协程完成,因此您不能使用job.join()。我猜Job 的主要意图是取消子协程。将runBlocking 块更改为以下内容将起作用:

runBlocking {
    job.children.forEach {
        it.join()
    }
}

【讨论】:

  • 虽然这可行,但我很确定,这不是预期的解决方案。 join 的文档明确指出,子作业也必须是完整的。此外,由于主管本身没有分配作业,因此应在开始作业后立即切换到已完成状态。在启动子作业和调用 join() 时都会启动作业。在开发过程中的某个时刻,存在一个名为 joinChildren() 的函数。但它现在消失了。
  • 但是是的,因为我可能没有理解完全错误的东西,所以我必须这样做
【解决方案2】:

你混合了两个角色:

  1. 在协程范围内发现的主作业永远不会自行完成,用于控制其他一切的生命周期
  2. 对应于一个工作单元的作业,可能分解为更多子作业

两个都需要,像这样:

val masterJob = SupervisorJob()
val scope = CoroutineScope(localDispatcher + masterJob)

val unitOfWork = scope.launch { handleEntry(node, scope) }
runBlocking { unitOfWork.join() }

上面的代码并没有真正激发主作业的存在,因为您只从它开始了一个子作业,但在更广泛的情况下它可能是有意义的,您有一些上下文可以启动许多作业,并且希望会写

masterJob.cancel()

在完成之前取消所有内容。

【讨论】:

  • 实际上我使用了主管作业,因为我从中启动了多个子作业(handleEntry 递归地启动更多作业),并且我希望它们能够独立失败而不会中断整个过程。
  • 但是解决方案不起作用。主作业不是无限期地阻塞,而是等待 handleEntry() 完成并立即切换到完成状态:join() 返回。但是,在 handleEntry 中启动的其他作业(使用范围参数)仍在运行。
  • 您是在谈论我的代码还是您的代码? masterJob 不等待任何事情完成,因此它永远不会改变状态。 unitOfWork 将失败传播给其他协程,这是真的。所以你的情况实际上不是标准的,因此masterJob.children.forEach { it.join() } 的方法是正确的。
  • 好的,但是将masterJob 添加到范围有什么意义呢?
  • masterJobSupervisorJob,这就是为什么它不会传播您的子协程的失败。它也是在其范围内启动的所有协程的收集器,因此您可以join它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-04
  • 2023-03-10
  • 2019-12-25
  • 1970-01-01
  • 2017-09-12
  • 1970-01-01
相关资源
最近更新 更多