Kotlin 协程源码学习笔记

本文是关于Kotlin协程源码的学习笔记,探讨了协程与线程的区别,介绍了协程的基本使用方法,如launch、runBlocking和delay。文章重点分析了协程的启动流程,包括创建Coroutine、使用EventLoop、CoroutineContext以及协程的挂起和恢复机制。通过对runBlocking、createCoroutineUnintercepted、intercepted、resumeCancellable等方法的剖析,揭示了协程内部的工作原理。
摘要由CSDN通过智能技术生成

Kotlin Coroutine协程源码学习笔记

学习kotlin有一段时间了,写个博客记录一下。本文基于kotlin 1.3.31来写,如有任何描述不当的地方,请多多指教纠正。

什么是协程

协程是一种轻量级的线程。那和线程有什么不一样?一.线程的调度是由操作系统负责的,它的睡眠、等待、唤醒都是由操作系统控制,开发者无法决定,而协程则可以由开发者决定代码切换的时机;二.线程要放弃CPU使用时间必须要进入阻塞状态,但阻塞的代价是昂贵的,而相比较而言协程可以通过挂起来切换代码,它相比阻塞而言,代价小很多;三.协程运行在线程中,一个线程中可以包含多个协程。

协程基本使用方法

  1. 使用GlobalScope.launch、runBlocking之类的协程构建器构建协程。
fun main(){
   
	//启动一个协程并且不会阻塞当前线程
	GlobalScope.launch {
   
		println("start a Coroutine in GlobalScope.launch ")
    }
    //启动一个协程并且阻塞当前线程,直到协程里面所有任务完成
   	//它被设计作为阻塞代码块和挂起风格的库之间的一个桥梁,因此一般用在main
   	//函数或者测试代码中,而不应该在协程中启动。
    runBlocking {
   
        println("start a Coroutine in runBlocking")
    }
}
  1. 在协程内可使用delay方法或者其他带suspend修饰符的方法表示挂起。带suspend修饰符的方法只能用在协程内,否则会编译报错。

  2. 可以用一个变量引用该协程返回的值(Job),然后在启动该协程的父协程内的合适的位置调用Job的join方法,那么该作业就会在合适的时机调用。

fun main(){
   
    GlobalScope.launch {
   
        println("start a Coroutine in GlobalScope.launch ")
        //开启一个运行在线程池中的协程
        val b= async{
   
            println(" async start")
            println("async end")
        }
        println("GlobalScope.launch running")
        b.join()
        println("end a Coroutine in GlobalScope.launch ")
    }
}
//输出结果:
//	start a Coroutine in GlobalScope.launch 
//	GlobalScope.launch running
//  async start
//	async end 
//	end a Coroutine in GlobalScope.launch
  1. 一个协程中使用多个suspend函数时,函数会按顺序执行。假如需要异步并发执行,使用async。

协程源码分析

协程内部是怎么实现的?使用delay之类的挂起函数,协程是怎么挂起以及恢复的?

接下来从一段代码开始分析:

 // 新建并启动 blocking 协程,运行在 main 线程上,等待所有子协程运行完成后才会结束
fun main() = runBlocking<Unit> {
   
    println("${Thread.currentThread().name} : runBlocking start")
    // 新建并启动 async 协程,运行在 Dispatchers.Default 的线程池中
    val b = async(Dispatchers.Default) {
    
        println("${Thread.currentThread().name} : async start")
        println("${Thread.currentThread().name} : async end")
    }
    b.await()
    println("${Thread.currentThread().name} : runBlocking end")
}

从runBlocking开始,主要的流程如下:

  1. 在创建一个协程Coroutine之前,先为它准备一些必要的元素,EventLoop以及CoroutineContext;
  2. 调用Coroutine.start启动协程;
  3. 调用Coroutine.joinBlocking循环获取要处理的事件。
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
   
    val currentThread = Thread.currentThread()
    val contextInterceptor = context[ContinuationInterceptor]
    val eventLoop: EventLoop?
    val newContext: CoroutineContext
    if (contextInterceptor == null) {
   
        // create or use private event loop if no dispatcher is specified
        eventLoop = ThreadLocalEventLoop.eventLoop
        newContext = GlobalScope.newCoroutineContext(context + eventLoop)
    } else {
   
        // See if cont
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值