day 4 I'm your father, coroutine父子继承关系

上面讲到job会由系统分配,但为什麽我们又能把job当作参数传入coroutine呢?

继承

在前面讲过,我们可以在coroutine再建立coroutine,并在新的CoroutineScope建立时,依然能透过这些可选的建构子,改变子coroutine的行为,那麽我们没有声明的coroutine context element怎麽实现呢?

从parent CoroutineContext,而正式的建构公式是
Parent context = Defaults + inherited CoroutineContext + arguments

啥?我建构的child coroutine会从parent CoroutineContext继承,然後做出... Parent context?
meme

对,我第一次看到也蒙了,让我现在解释解释

coroutine context inherent

我不得已,还是用ppt做了图出来,这边转化成code会是这样

val scope = CoroutineScope(Job())
scope.launch ( Dispatchers.IO ){

}

听我娓娓道来,为甚麽会有三个context要看

当你在coroutineScope里面创建coroutine时,他会先继承parent coroutineContext的值,再用你传入的建构参数复写,这边是Dispatcher. IO,这时就会出现图中的Parent context,但这时还没结束,在parent context里面的是父coroutine的Job(),而针对我们新建立的coroutine,系统会分配一个新的Job给他

The resulting parent CoroutineContext has Dispatchers.IO instead of the scope’s CoroutineDispatcher since it was overridden by the argument of the coroutine builder.

Also, check that the Job in the parent CoroutineContext is the instance of the scope’s Job (red color), and a new instance of Job (green color) has been assigned to the actual CoroutineContext of the new coroutine.

这关系到coroutine context的继承,当你launch一个coroutineScope会回传一个job,在coroutine里面创建coroutine也会回传一个Job

val rootJob = CoroutineScope(Job()).launch{
    val job = launch{
    
    }
}

先看一下,创建一个CoroutineScope会回传了一个scope,launch之後回再回传一个Job

但此Job非彼Job,可以看看这精美的comment,我直接切重点

Launches a new coroutine without blocking the current thread and returns a reference to the coroutine as a [Job].

看到重点没returns a reference of the new coroutine大概就是这样
回传的Job是a reference of the new coroutine,传入的Job是CompletableJob,传入後他会在CoroutineContext这个indexed set找job,如果没有,他就自己建立实例

听我讲半天,还是印个log出来给你看

val thirdJob = Job()

viewModelScope.launch{
    val a = launch (thirdJob) {
    }
    Timber.d("third")
    Timber.d(thirdJob.toString())

    Timber.d("a")
    Timber.d(a.toString())
}
D/CoroutineFragment$test: third
D/CoroutineFragment$test: JobImpl{Active}@d02612f
D/CoroutineFragment$test: a
D/CoroutineFragment$test: StandaloneCoroutine{Active}@cf9e33c

既然已经分清楚Job了,还是必须解释为什麽是这样设计的?

记得我们说过Job是用来控制和管理coroutine的生命周期、行为等等的吧!如果每次继承,都用同一个Job,那问题就大了,你完成任务的coroutine不能被取消,占着记忆体空间不做事,你没有办法对各coroutine做出不同行为,如等待结果再执行,等等等等

当我们在一个coroutine里面再建立coroutine时,会先继承到parent context的Job,这步骤基本上是再说
coroutine-parent

而新建的coroutine并不会是他的parent coroutine,系统会自动配发新的Job,让我们有需要时再用变数辨别、操作,极大限度地减少开发者要完成的工作,同时也更加的灵活,符合structured concurrency的设计

继承的理解盲点

假设这样一段code,child1的parent是job,而supervisorJob实际上毫无作用,这次因为每个新的协程会被系统分配新的job实例,在这个范例中覆写了supervisorJob()

val scope = CoroutineScope(Job())
scope.launch(SupervisorJob()) {
    // new coroutine -> can suspend
   launch {
        // Child 1
    }
    launch {
        // Child 2
    }
}


supervisorJob或是supervisorScope只有在创建scope时传入才有效,下一篇会讲讲如何正确的用supervisor

官方blog corotuine first thing first


<<:  Day11 配对品质评估 Evaluator

>>:  Day 07 : 资料视觉化 Matplotlib

【Day5】注册画面 X Firestore Database

昨天我们已经把登入画面做好了,大家有没有觉得万事起头难呢? 既然我们已经有登入画面了,当然要有注册...

Day10:终於要进去新手村了-Javascript-变数

这篇内容会讲关於变数的部分 变数简单来说,就是一个可以放资料的空间,它的用途就是用来储存资料,以及要...

Day 12 - 下单电子凭证及Stock股票Order建立

本篇重点 api.activate_ca 启动下单电子凭证 Stock股票Order建立 api.a...

[Day30] 总结 - 铁人带给我了什麽?

很开心能走到这一天!日更真的是不太容易,期望自己下次能有些库存之後,再来参加 XD。 为什麽挑战写铁...

卡夫卡的藏书阁【Book29】- Kafka - MirroMaker 2

“I need solitude for my writing; not 'like a herm...