协程上下文与Job深入解析

在上一次https://www.cnblogs.com/webor2006/protected/p/12611978.html对于协程的调试与线程之间的切换进行了相关的学习,这次进一步对Job进行进一步的学习,在之前https://www.cnblogs.com/webor2006/p/11730528.html其实已经对Job有了一定的了解,简单回顾一下:

当时咱们举的一个等待协程代码执行完利用到了Job.join()这个方法的例子, 其实关于Job还有更加深入的东西待挖掘,下面来看一下。

Job深入解析:

在上次的咱们使用Job的例子中是通过调用"GlobalScope.launch()"来获得Job对象的:

因为GlobalScope.launch()本身就是返回的Job对象:

但是!!对于没有明确返回值Job的场景那又有什么方法能获取到Job本身呢?比如说:

这个runBlocking的函数的返回值并非是Job的:

此时先来上一个理论说明:

协程的Job是归属其上下文(Context)的一部分,Koltin为我们提供了一种简洁的手段来通过协程上下文获取到协程自身的Job对象。我们可以通过coroutineContext[Job]表达式来访问上下文中的Job对象。

还是有些抽象,下面用例子来进行说明:

运行看一下:

此时咱们可以添加上一次https://www.cnblogs.com/webor2006/protected/p/12611978.html学习的协程调试的JVM参数再来看一下协程的信息:

再运行:

程序虽简单,但是这里面蕴藏的细节还是相当多的,目前而言存在如下几个疑点:

1、coroutineContext从哪来的,为啥可以直接使用?

2、为啥可以直接使用[]来调用,背后我猜肯定是用了运算符重载了,实际是否如猜想呢?

3、 为啥咱们可以直接使用这个Job,它又是哪里来的?

4、为啥输出结果是它?

以上四个问题,下面则来一一进行剖析。

剖析Job协程上下文的原理:

接下来则从上面的四个疑问的问题寻找其整个背后的原理。

疑问一:coroutineContext从哪来的,为啥可以直接使用?

点击进去查看一下它:

原来它是在CoroutineScope接口里所定义的一个属性, 而CoroutineScope接口有很多具体的实现类,根据咱们的输出结果来看看到了一个“BlockingCoroutine”,它就是具体的子类之一:

打开瞅一下这个具体类:

而这个context的值又是来自于上面这两个属性来构成:

而runBlocking()里面的代码块就是运行在CoroutineScope中的:

很明显咱们就可以在这个协程代码块中直接使用coroutineContext了:

咱们再来读一下该属性的说明:

从文档说明中也能看到,代码中要访问这个属性唯一的使用就是访问Job,所以正好是咱们目前所使用的代码形式,目前此问题就已经解决了。

疑问二:为啥可以直接使用[]来调用,背后我猜肯定是用了运算符重载了,实际是否如猜想呢?

这里还是从源码来找答案:

此时看一下CoroutineContext这个类:

而看它里面有一个操作符的重载,如我们所猜想:

 

所以这也是为啥咱们是用的可空类型来接收:

关于operator fun的写法在之前https://www.cnblogs.com/webor2006/p/11369333.html的属性委托有过使用,而之所以可以使用[]表达式来访问,这个语法在之前倒还木有学到,这里参考一下这位博主的说明:https://www.jianshu.com/p/e265dbcfe009,其中看一个例子就明白了:

而我们访问时就可以这样:

回到咱们这个重载函数的定义:

很明显咱们也可以用[]来访问这个重载符函数,其中接收的参数是一个Key类型,所以最终我们看到这样的写法了:

此问题就已经明白了。

疑问三:为啥咱们可以直接使用这个Job,它又是哪里来的?

接下来解决最后一个疑点,既然我们在第二个问题分析中可以看出实际是要传一个Key类型的,但是为啥我们传了一个Job呢?

 

Job和Key之间肯定有联系,下面来剖析一下:

咱们先来看一下Key的定义:

 

好,此时再来看一下我们调用时传递的Job的定义:

很明显,我们直接能传递这个Job的原因就是由于在里面定有伴生对象所达成的,关于伴生对象可以参考https://www.cnblogs.com/webor2006/p/11210181.html,这块的答案就可以揭晓了。

疑问三:为啥输出的Job是BlockingCoroutine?

点击瞅一下它的定义:

而这个BlockingCoroutine的继承体系如下:

 

所以,最后这个疑点也解决了。

最后再来修改一下程序:

 

然后咱们来看一下这个isActive的定义:

而从它的实现细节就可以看到确实如文档所说:

咱们来试一下:

而这个的isActive是定义在Job里面的:

那,研究这个细节有啥意义呢?结论就是:对于协程是否是isActive其实是取决于它的Job是不是isActive

以上则对于这个非常简单的例子来完整的阐述了它里面背后的原理,虽说程序简单,但是要搞清楚其背后机制还是不简单的,通过这样的一个彻底分析,对于协程、协程上下文、Job之间的关系也更进一步的。

原文地址:https://www.cnblogs.com/webor2006/p/12652844.html