在不使用显式 Job() 对象的情况下取消父作业

Posted

技术标签:

【中文标题】在不使用显式 Job() 对象的情况下取消父作业【英文标题】:Cancelling parent job without using explicit Job() object 【发布时间】:2020-10-02 05:33:54 【问题描述】:

在下面的代码中,我创建了两个作业,然后我取消了父作业。我希望子作业会被取消,但是,子作业不会被取消,它们仍然处于活动状态。为什么?

注意:我知道这个问题很容易解决,方法是创建一个 Job() 对象,然后将 Job 对象提供给协程上下文,然后取消 Job() 对象。我的问题是为什么下面的代码不起作用?如果我想取消父作业,我必须使用显式父参数吗?

fun main() 

    // CoroutineScope
    val coroutineScope = CoroutineScope(Dispatchers.IO)

    var child1 : Job? = null
    var child2 : Job? = null

    // Use
    val currentJob = coroutineScope.launch 
        child1 = coroutineScope.launch 
            delay(500)
        
        child2 = coroutineScope.launch 
            delay(500)
        
    

    Thread.sleep(300L)

    currentJob.cancel()

    println("Job 1 state: $child1?.status()")
    println("Job 2 state: $child2?.status()")
    println("Parent job is active: $coroutineScope.isActive")
    println("Parent job is active: $currentJob.isActive")


    Thread.sleep(2000L)

输出:

Job 1 state: Active
Job 2 state: Active
Parent job is active: true
Parent job is active: false

【问题讨论】:

【参考方案1】:

child1child2 作业不是 currentJob 的子级。他们是coroutineScope Job 的孩子。我们可以说currentJobchild1child2 是“兄弟姐妹”。如果您想全部取消它们,请取消 coroutineScope 而不是:coroutineScope.cancel() 。或者让他们成为currentJob的孩子并取消currentJob

val currentJob = coroutineScope.launch 
    child1 = launch 
        delay(500)
    
    child2 = launch 
        delay(500)
    


Thread.sleep(300L)

currentJob.cancel()

【讨论】:

@oiyio 在内部传递的 CoroutineScope 对象上调用启动,该对象将这些任务作为子项注册到其作业中。 天哪。我现在意识到了...好吧,您建议的代码会按我的预期打印孩子。但是 coroutineScope.isActive 返回仍然是 true。为什么? @oiyio 因为您只取消了currentJob,这将取消child1child2。试试这个coroutineScope.cancel() 取消整个范围。

以上是关于在不使用显式 Job() 对象的情况下取消父作业的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Rails 中使用延迟作业取消预定作业?

在不使用显式锁的方式下使用多线程

编译器构造:显式解析树

如何在没有显式比较对象的情况下从 Container 对象构造 std::priority_queue 的实例?

为啥 vuejs 子组件数据更改更新父数据也没有显式 $emit?

如何在不显式导入的情况下使新的装饰器在类中可用?