Kotlin Coroutine (二)

上一篇说完了协程的整个生命周期,总结一下:

  • 用Coroutine Builder函数(launchstartCoroutine等)创建协程
    • 库内部使用createCoroutineUnchecked来创建协程,需要借助编译器的帮助
  • 协程中的指令开始顺序执行
  • 调用另一个suspend函数,直到调用到suspendCoroutine系列的函数
    • 如果suspendCoroutine在同步返回前调用了Continuation.resume/resumeWithException,则会像普通的函数一样正常的返回/抛出异常,resume/resumeWithException的参数会作为返回值/抛出的异常。然后继续执行协程中的代码
    • 如果返回前没有调用两者中的任何一个,那么协程中后续的代码会被全部跳过,直接从coroutine builder函数中返回,继续执行调用builder的方法中的后续代码。
      • suspendCoroutine系列的方法会调用suspendCoroutineOrReturn,which is 一个看不到源码内部方法
  • 如果被suspend了,协程会在未来某个时刻之后其resume/resumeException之后被继续执行,这时的行为和在suspendCoroutine中同步地resume/resumeWithException的效果一样。唯一的不同之处在于,之后的代码可能在于前半部分不一样的线程上执行。这取决于这个协程的Context中的CoroutineDispatcher的行为。
  • 整个协程执行完后,coroutine对象(准确说是Continuation)会普通地被gc掉。如果一个暂停的协程始终没有resume,那么如果它的引用没有被持有,也会被回收掉;否则,将一直存在在内存中。

来一个例子

fun main(args: Array<String>) {
    launch {
        println(suspendFunc1())
        println(suspendFunc2())
        println(suspendFunc3())
        println("coroutine is about to exit")
    }
    println("launch has returned")
    Thread.sleep(200) // make sure that the main thread won't exit before coroutine exit
}

suspend fun suspendFunc1() : Int {
    return suspendCoroutine { cont ->
        println("before thread")
        thread {
            Thread.sleep(100)
            cont.resume(1)
        }
    }
}

suspend fun suspendFunc2() : Int {
    return suspendCoroutine { cont ->
        cont.resume(2)
    }
}

suspend fun suspendFunc3() : Int {
    return 3
}

这段会输出

launch has returned
before thread
1
2
3
coroutine is about to exit

过程:
launch将传入的lambda创建为协程,生成Continuation对象
-> 调用suspendFunc1,注意此时协程并不会暂停!
-> suspendFunc1中调用suspendCoroutinesuspendCoroutine将代表当前协程的Continuation传给后面的代码段
-> 代码段中新建了一个线程。这个线程会在0.1毫秒后调用Continuation的resume
-> 那么这个代码段就在没有调用resume的情况下直接返回
-> 所以此时这个协程被暂停,从launch中返回,输出launch has returned
-> 0.1ms后,resume被调用,1作为suspendCoroutine的返回值,整个函数继续执行,也就成了suspendFunc1的返回值。打印1
-> 调用suspendFunc2,再调用了suspendCoroutine,这一次代码段返回前调用了resume,所以协程没有被中断,suspendCoroutine直接返回了2,然后返回到launch的里面。打印2
-> 最后调用suspendFunc3,完全没有调用任何其他函数,所以和普通函数没有任何区别。打印3
-> 最后打印suspend is about to exit

那么before thread呢?我故意在上面没讲,因为这与CoroutineStart和CoroutineDispatcher的机制有关

CoroutineStart

  这表示协程开始时的行为。粗略地说(按照文档的说法),有C#和JS两种风格,前者在协程开始时仍在原来的协程上执行,在第一次暂停时才把协程拿走(就是不在当前线程上继续执行这个协程了),仅仅在resume时才让CoroutineDispatcher调度。后者在协程开始时立即就会用CoroutineDispatcher来将协程调度到某个线程上执行。换句话说,前者的launch会在第一次暂停时才会返回,后者的则总是会立即返回(准确说是把协程派发走之后)
  实际上Kotlin提供了好几种选项,具体行为自己看代码

CoroutineDispatcher

  其实上面说完了,CoroutineDispatcher就是在resume时(也可能在开始时就)调度协程到某个线程上继续执行。具体地说,它本身是一个ContinuationInterceptor,也就是它会拦截某个Continuation的resume操作,返回另一个Continuation(往往是把被拦截的那个包进另一个里面)。所以一般Interceptor也要有一个对应的Continatioin类。这个用来包裹的Continuation用来在调用被包裹的那个的resume之前插入一些逻辑。一个简单的例子:

class WrapperContinuation<T>(private val cont: Continuation<T> : Continuation<T> {
    fun resume(value: T) {
        someLogic(value)
        cont.resume(value)
    }

    fun resumeWithException(ex: Throwable) {
        someLogic(ex)
        cont.resumeWithException(ex)
    }
}

  而CoroutineDispatcher由库进一步封装了,用来包裹的Continuation已经提供了,只需要重载CoroutineDispatcher的dispatch方法就行了。(写到这儿我突然想起来第一篇好像已经提过了这个orz)总之就是把传进来的那个Runnable放到某个线程上执行就行了。
  
  库提供的launch采用了JS式的CoroutineStart行为,并且,在CoroutineContext里如果没有任何的Dispatcher的话,就用一个DefaultDispatcher作为派发器。这个类会把所有协程都放到CommonPool里(公用的ForkJoinPool)。
  所以这里就可以来回答上面的问题了。例子中的launch会立即返回,而before thread是从一个线程池里的线程打印出来的。所以launch has returnedbefore thread之前打印。
  而如果选择使用C#式的CoroutineStart,那么before thread就会在launch has returned之前打印,然后是1 2 3。
  (怎么好像和Dispatcher没什么关系orz,算了写了就写了)

好,那整个协程的完整执行过程差不多讲完了。接下来是一些实现细节。

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值