kotlin协程原理

1 什么是协程

轻量级线程,kotlin1.3版本后,提供了协程coroutine库,一种简化异步任务处理的方案。

2 为什么用协程

2.1 简化代码,增加可读性

使用协程可以用简洁直观可读性高的写法,实现多重依赖关系的异步任务的书写。

若不使用协程,一般的异步方式?

  • 通过Callback回调的方式
  • 利用AsyncTask
  • 通过链式调用
    java8提供的CompletableFuture
    使用RXJava这种链式实现多重依赖的异步任务,可以解决回调嵌套问题,相比于callback回调可读性好。
  • 新启线程
    .......

2.2 合理使用线程,减少性能损耗

  • 协程依赖于线程的使用,挂起协程不会阻塞线程,后续执行和复用,轻量级
  • 在协程默认的线程池中,处理逻辑更偏向在当前线程的任务队列中添加任务,而不是开启新的工作线程
  • 在多cpu情况下,会开启不超过cpu数的线程数并可以从其他线程的任务队列中抢夺任务,最大程度地复用已有的工作线程。

3 怎么使用协程

3.1 协程作用域

协程是一套管理和运行异步任务的框架,所以需要有运行的环境,也叫协程的作用域,在这个作用域里,才可以使用协程来执行异步任务。

(1) 全局环境

GlobalScope.launch {}

GlobalScope代表协程的全局作用域,在该作用域启动的协程为顶层协程,没有父任务,且该scope没有Job对象,所以无法对整个scope执行cancel()操作,

所以如果没有手动取消每个任务,会造成这些任务一直运行,可能会导致内存泄露等问题。

(2) 局部环境

CoroutineScope(Dispatchers.Main).launch {}
通常会通过创建CoroutineScope,来实现一个协程作用域,并且可以指定派发器,可以取消该scope下所有正在进行的任务。

3.2 协程派发器

kotlin提供的一些默认的Dispatcher

名称 说明
Dispatchers.IO 工作线程池,依赖于Dispatchers.Default,支持最大并行任务数
Dispatchers.Main 主线程,这个在不同平台定义不一样,所以需要引入相关的依赖,比如Android平台,需要使用包含MainLooper的handler来向主线程派发
Dispatchers.Default Dispatchers.Default
Dispatchers.Unconfined 无指定派发线程,会根据运行时的上线文环境决定

通用的1和2

3.3 启动协程任务

(1) 对于一个scope对象,常用launch和async创建协程。
      CoroutineScope(Dispatchers.IO).launch { }
      CoroutineScope(Dispatchers.IO).async { }
而两者的最大不同是,async会创建一个Deferred的协程,可以用来等待该协程执行完毕再进行后续操作。

(2) 内部协程

CoroutineScope(Dispatchers.IO).launch {
	async { 
	}
}


在一个协程内部,也可以创建子协程。

(3)改变协程任务执行环境
如IO线程执行异步请求,数据回来后在主线程进行展示。

CoroutineScope(Dispatchers.IO).launch {
	val dataJob = async {
		//request data
	}
	async(Dispatchers.Main) {
		//prepare
		val data = dataJob.await()
		//display with data
		}
	}
}

5.协程挂起
协程可以顺序完成异步任务,那么在等待上一个协程任务完成时,当前的协程需要被挂起(不阻塞线程)

CoroutineScope(Dispatchers.IO).launch {
	val dataJob = async {
		//request data
	}
	withContext(Dispatchers.Main) {
		//prepare
		val data = dataJob.await()
		//display with data
	}
	//do some post async job after display data
	async{ }
}


withContext()方法,除了可以指定启动协程任务外,还可以挂起当前协程,即外部的launch的协程,直到withContext启动的协程任务完成后,才会重新恢复外部的launch协程,执行下面的async语句。

使用suspend关键字

CoroutineScope(Dispatchers.IO).launch {
	//prepare
	val data = getData()
	//display with data
}
//挂起当前协程
private suspend fun getData() = suspendCoroutine {
	Call().addCallback(object : Callback {
		override fun onCall(res: String) {
			//恢复协程
			it.resume(res)
		}
	})
}

使用suspend关键字,表明方法可以被挂起,称为挂起函数suspendCoroutine方法,会挂起当前的协程,并把挂起的协程返回到代码块的参数中,代码块执行自定义逻辑。

4  协程实现原理

4.1 基本原理

三剑客:协程作用域,dispatcher,coroutine

简化结构关系

kotlin协程原理_第1张图片

 

详细关系图:

kotlin协程原理_第2张图片

kotlin协程原理_第3张图片

基本流程:在作用域启动协程,调度器调度,若协程挂起,则调度其他协程或者执行其他主协程代码

 

你可能感兴趣的:(kotlin)