抽丝剥茧聊Kotlin协程之协程是如何建立结构化并发的

简介: 抽丝剥茧聊Kotlin协程之协程是如何建立结构化并发的

1. 协程的结构化并发


上篇文章,我通过两个例子简单的介绍了Job cancel方法在不同的父子关系情况下,看起来很相似的代码,执行结果却很不相同的情况。文中我引出了Job结构化并发的概念,即父Job和子Job形成树的数据结构,本文我将详细介绍Kotlin协程框架是如何实现结构化并发的。


什么叫结构化并发?用通俗易懂的话解释就是,协程之间的协作是有组织,有纪律的。如果协程之间的关系是确定了的,那么协程之间的cancel和exception处理是有章法可寻的。


下图,假设Job2处发生了异常,那么它的子Job、父Job以及父Job的其它子Job是否会被cancel掉呢?答案是看情况(不是说有章法可寻吗?怎么成看情况了?因为算法是固定的,但是Job的行为可能各不相同)。在树的数据结构中,任何一个节点的cancel事件有两条传播路径,向上传播给父节点,向下传播给子节点。那么接受到cancel事件的父节点或子节点是否cancel掉自己完全取决于它的内部实现。


image.png

举例说明,假设Job2处发生异常了协程被迫cancel掉,事件传播到Job0。那么Job0会有两种处理方式。其一、把自己也cancel掉,然后把自己的所有子Job都cancel掉,其二、忽略掉该事件,当作啥事也没发生。


熟悉协程的同学应该能意识到,这两种处理方式分别对应Job和SupervisorJob。


1.1 Job处理异常


image.png

job2处1/0发生异常打印结果如下,我们注意到通过Job方式启动,随着job2发生异常,job1和job3都被cancel掉。


2021-12-19 16:35:52.439  job1 start
2021-12-19 16:35:52.440  job2 start
2021-12-19 16:35:52.440  job3 start

1.2 SupervisorJob处理异常


image.png

打印结果如下,job1和job3不受job2的异常影响,程序照常执行。


2021-12-19 16:44:30.832  job1 start
2021-12-19 16:44:30.833  job2 start
2021-12-19 16:44:30.833  job3 start
2021-12-19 16:44:32.835  job1 end
2021-12-19 16:44:32.835  job3 end


那么问题来了:为什么SupervisorJob和Job会有这样的差异呢?


2. Job基础知识



image.png

通过CoroutineScope.launch方法启动协程,返回值为Job类型。本文着重讲解以下几个方法:


  1. start
  2. cancel
  3. invokeOnCompletion


2.1 start方法


start方法作用是启动一个协程,类似Thread.start方法。通过默认的方式启动协程,start方法会默认被调用的。


2.2 cancel方法


cancel方法作用是取消协程,避免资源的浪费,Android开发者比较熟悉的场景有,当一个Activity销毁之后,应该取消掉还没有执行完的工作。协程中的cancel,必须要有挂起点。


2.2.1 使用delay方法


image.png

image.png

观察日志由于delay方法是suspend修饰的,它是协程的挂起点,发现job1被成功cancel掉。


2021-12-19 17:16:16.377  running in job2


2.2.2 使用Thread.sleep方法观察发现,job1协程体没有调用suspend修饰的方法,无法被cancel掉,job1


image.png

2021-12-19 17:26:05.554  running in job2
2021-12-19 17:26:06.544  running in job1


2.2.3 通过yield或者ensureActive方法更正


在Thread.sleep方法后面增加yield或者ensureActive方法,可以让job1被cancel掉。yield本身是一个suspend函数,在yield方法处恢复协程体运行时,会检查当前协程是否被cancel掉。ensureActive则是通过抛出CancellationException取消掉当前的协程。


image.png

image.png

2.2.4 CancellationException是一种特殊的Exception,它和Exception的区别在于,如果在协程中抛出CancellationException,除了子协程外并不会影响其它协程


image.png

对比1.1代码1/0抛出除0异常,CancellationException并不会影响job1和job3。


2021-12-19 19:08:46.558  job1 start
2021-12-19 19:08:46.559  job2 start
2021-12-19 19:08:46.561  job3 start
2021-12-19 19:08:46.626  job4 start
2021-12-19 19:08:48.560  job1 end
2021-12-19 19:08:48.561  job3 end

2.3 invokeOnCompletion方法


该方法的作用是给Job注册一个回调,当Job执行完成后执行回调函数。


image.png

2021-12-19 19:19:39.278  job start 2021-12-19 19:19:41.280  job end 2021-12-19 19:19:41.281  job invokeOnCompletion

image.png

当协程被cancel时,回调立马执行。


2021-12-19 19:34:21.809  job start
2021-12-19 19:34:22.811  job invokeOnCompletion


我们来看下invokeOnCompletion源码


image.png

CompletionHandler类似Java中的callback,当Job完成后,调用该回调,类似Android的OnClickListener。CompletionHandler持有ChildJob,并注册到父Job上,那么子Job就可以监听父Job的取消和完成事件,从而实现事件从上往下传播。如果子Job能够直接或间接的持有父Job的引用,那么当子Job被cancel时,就能直接把事件,从下往上传播。所以cancel事件是双向传播的


而协程框架也正是通过invokeOnCompletion方法建立起父子Job的树形关系


3. Job建立树形关系



image.png

image.png

image.png

我们关注上面类图标红的几个类。


划重点


  1. ChildHandle的childCancelled表示子Job被取消时,往上传播
  2. CompletionHandler的invoke方法表示父Job被取消时执行子Job的回调,往下传播,最终会调用到ChildJob的parentCancelled方法
  3. ChildHandleNode同时实现了ChildHandle和CompletionHandler,表示该节点可以双向传播

核心方法有:


  1. JobSupport.initParentJobInternal(parent: Job?)
  2. Job.attachChild(child: ChildJob): ChildHandle
  3. JobSupport.invokeOnCompletion( onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler )


image.png

image.png

划重点->通过CompletionHandler生成JobNode设置到父Job的state属性中,state属性有NodeList链表,用来保存回调列表

image.png

image.png

1处,如果当前只有一个CompletionHandler,则直接保存到state中

2处,如果当前有两个CompletionHandler则将state属性提升成链表

3处,如果当前大于2个CompletionHandler,则直接将该CompletionHandler添加到链表的尾部。


PS这块代码看起来比较复杂,写起来也比较费劲,初次接触肯定云里雾里,时间有限先写到这里吧。以后时间允许再详细写写


总而言之


1. Job通过state的NodeList对象与子Job建立关系,当Job被取消时通过这个链条向子Job发送取消请求


2. Job通过initParentJobInternal方法中获取到的parentHandle与父Job建立关系,当子Job被取消时通过该引用向父Job发送取消请求


3. 协程Job中的双向传播机制是协程框架算法决定的。当取消请求到达具体的Job时,Job如何处理则由Job实现类自己决定


4. 本文讲解了Job是如何建立关系的。下篇文章我将结合Job,SupervisorJob,coroutineScope,supervisorScope讲解他们在关系链路上如何处理双向cancel事件的

相关文章
|
1月前
|
安全 Android开发 开发者
构建高效Android应用:Kotlin与协程的完美结合
【2月更文挑战第30天】在移动开发领域,性能优化和流畅的用户体验是关键。本文深入探讨了如何通过结合Kotlin语言和协程技术来提升Android应用的性能和响应能力。我们将分析Kotlin的优势,介绍协程的基本概念,并通过实际案例展示如何在应用中实现协程以简化异步编程,从而提供更加高效的解决方案。
|
1月前
|
移动开发 Java Android开发
构建高效Android应用:Kotlin与协程的完美融合
【2月更文挑战第25天】 在移动开发领域,性能优化和应用响应性的提升是永恒的追求。随着Android Jetpack组件库的不断丰富,Kotlin语言已经成为Android开发的首选。而Kotlin协程作为一种新的并发处理方案,它以轻量级线程的形式,为开发者提供了简洁高效的异步编程手段。本文将深入探讨Kotlin协程在Android应用中的实践运用,以及如何通过这种技术改善用户界面的流畅度和后台任务的处理能力,进而构建出更高效、更稳定的Android应用。
|
1月前
|
程序员 Go 数据处理
|
1月前
|
移动开发 调度 Android开发
构建高效Android应用:Kotlin与协程的完美结合
【2月更文挑战第21天】随着移动开发技术的不断进步,Android平台正寻求更高效、更简洁的编程解决方案。Kotlin作为一种现代编程语言,以其简洁性和对函数式编程的支持赢得了开发者的青睐。而协程作为处理异步任务的强大工具,在提升应用性能方面显示出巨大潜力。本文将深入探讨Kotlin语言和协程技术如何相辅相成,帮助开发者构建更加流畅和高效的Android应用。
|
1月前
|
API 数据库 Android开发
构建高效安卓应用:Kotlin与协程的完美融合
【2月更文挑战第20天】在移动开发领域,性能优化和资源管理始终是开发者关注的焦点。随着Kotlin语言在Android平台的普及,协程作为其核心特性之一,提供了一种全新的异步编程范式。本文将深入探讨如何通过Kotlin协程提升安卓应用的性能,减少内存消耗,并实现流畅的用户体验。我们将分析协程的原理,展示其在实际应用中的效能,并提供实践指南,帮助开发者掌握这一强有力的工具。
14 0
|
1月前
|
安全 Java Android开发
构建高效Android应用:Kotlin与协程的完美融合
【2月更文挑战第15天】随着移动开发技术的不断进步,Android平台的开发者寻求更高效的编程解决方案以提升应用性能和用户体验。Kotlin作为官方推荐的开发语言,以其简洁性和功能安全性受到青睐。而协程,作为一种轻量级的线程管理机制,在异步编程和网络请求等方面展现出巨大潜力。本文将深入探讨Kotlin语言结合协程如何优化Android应用的性能,包括提高响应性、简化异步代码结构以及减少资源消耗等方面,旨在为开发者提供实战指南,帮助他们构建更加流畅和高效的Android应用。
|
2月前
|
存储 安全 Java
深入浅出Go并发之协程—goroutine
深入浅出Go并发之协程—goroutine
36 0
|
2月前
|
开发者 Python
深入浅出Python协程:提高并发性能的秘诀
在现代软件开发中,提高应用程序的并发处理能力是一项挑战,尤其是在资源受限的情况下。本文将探讨Python协程的概念、原理及其在提高并发性能中的应用。通过对比传统多线程和多进程模型,我们将揭示Python协程如何以更少的资源消耗实现高效的并发任务处理。文章还将通过实例代码演示协程的创建、事件循环的管理以及异步编程的最佳实践,旨在为读者提供一种更轻量级、更高效的并发编程方法。
|
2月前
|
程序员 API 调度
深入理解Python协程:提高并发性能的关键
在现代软件开发中,提高应用性能和响应速度已成为开发者不断追求的目标。本文将深入探讨Python协程(Coroutine)机制,一种轻量级的并发编程解决方案,旨在帮助读者理解其工作原理、实现方式及如何在项目中有效利用协程来提高程序的并发性能。通过对比传统多线程和进程的并发模型,文章将展示协程如何以更低的资源消耗实现高效的并发任务处理,从而为开发高性能应用提供了新的视角和方法。
35 0
|
2月前
|
开发者 Python
深入浅出Python协程:提高并发性能的利器
本文旨在深入探讨Python中的协程机制,一种轻量级的并发编程解决方案。与传统的多线程和多进程相比,协程提供了更高效的并发性能,尤其是在I/O密集型应用中。我们将从协程的基本概念入手,解析其工作原理,并通过实例讲解如何在Python中使用协程来优化程序性能。文章还将对比协程与其他并发模型的优缺点,帮助读者全面理解协程在现代软件开发中的应用价值。
27 3