【Kotlin学习】协程的基本概念(1)

协程的基本概念

协程定义

它是一种用户态的轻量级线程,协程的调度完全由用户控制。它有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁地访问全局变量,所以上下文的切换非常快。协程虽然是微线程,但并不会和某一个线程绑定,它可以在A线程中执行,经过某一个时刻的挂起,等下次调度到恢复执行的时候,很可能会在B线程中执行。

线程和协程的区别

线程的阻塞代价是昂贵的,而协程使用了更简单、代价更小的挂起来代替阻塞

协程常用基本概念

1.CoroutineContext
协程上下文,它包含一个默认的协程调度器,所有协程都必须在CoroutineContext

2.CoroutineScope
协程作用域,它一个接口只包含一个属性coroutineContext。它定义了一个协程的作用范围。每个Coroutine builder都是CoroutineScope的扩展,CoroutinesScope会在具有生命周期的实体上实现。Kotlin定义一个全局的作用域GlobalScope,用于启动顶级的协程,这些协程会在整个应用程序生命周期内运行

3.CoroutineDispatcher
协程调度器,它用来调度和处理任务,决定了相关协程应该在哪个或哪些线程中执行,kotlin的协程包含多种协程调度器

4.Suspend
关键字,协程可以被挂起而无需阻塞线程。我们使用suspend关键字来修饰可以被挂起的函数。被标记为suspend的函数只能运行在协程或者其他suspend函数中。suspend可以修饰普通函数、扩展函数和Lambda表达式

5.suspension point
协程每个挂起的地方是一个suspension point

6.Continuation
字面意思是继续、持续的意思。由于协程可能是分段执行的:先执行一段,挂起,再执行一段,再挂起…相邻的两个suspension point之间被称为Continuation。Continuation用来表示每一段执行的代码,一个完整的协程程序包含多个Continuation

7.Job
任务执行的过程被封装成Job,交给协程调度器处理。Job是一种具有简单生命周期的可取消任务,当父类的Job被取消时,子类的Job也会被取消,Job拥有三种状态:isActive、isCompleted、isCancelled

8.Deferred
是Job的子类,Job完成时是没有返回值的,而Derferred在任务完成时能够提供返回值

在这里插入图片描述

Coroutine builders

每一个Coroutine builders是一个函数,它接受一个suspend的Lambda表达式并创建一个协程来运行它

launch和async

1.创建协程

在kotlin中创建携程很简单,它会启动一个新的协程,返回一个Job对象

在这里插入图片描述

async跟launch的用法基本一致,使用async会返回一个Deferred对象。因此async创建的协程拥有返回值,通过await()方法将值返回(若要在线程中返回值,直接使用Thread会比较麻烦,可考虑使用Future)

在这里插入图片描述

2.start参数

launch、async的start参数用于指定协程应该何时开始

查看async源码

在这里插入图片描述

在默认情况下,当执行到具有launch、async函数时,会立即启动协程内部的工作,因为start参数的默认值是CoroutineStart.DEFAULT。当start参数使用CoroutineStart.LAZY值时,只有在返回的Job或Deferred对象显式调用start()join()await()(只有Deferred才有await())时才会启动协程,类似于懒加载

在这里插入图片描述

如果上述代码最外层launch函数的start参数也用了CoroutineStart.LAZY,则result的结果不会打印,因为最外层launch返回的job对象并没有被调用

3.invokeOnCompletion函数

launch、async的invokeOnCompletion函数会对它们的执行结果进行回调,支持异常的处理

在这里插入图片描述
在这里插入图片描述

其中cancelAndJoin()表示取消任务。由于res已经被取消了,因此res.await()获取不到结果。最后,Job、Deferred对象都可以取消任务(使用cancel()、cancelAndjoin())

runBlocking

runBlocking创建的协程直接运行在当前线程上,同时阻塞当前线程直到结束。在runBlocking内可以创建其他协程,例如launch。但是反过来,在launch中使用runBlocking则不行。调用了runBlocking的主线程会一直阻塞直到reunBlocking内部的协程执行完毕

在这里插入图片描述

runBlocking在使用时可以指定CoroutineDispatcher,此时创建的协程会在指定的协程调度器中运行,同时阻塞当前线程。runBlocking最后一行的值即为它的返回值

挂起函数

协程提供了避免阻塞线程,并用更廉价、更可控的操作替代线程阻塞的方法:协程挂起
挂起函数比普通函数多了一个关键字suspend修饰,kotlin的挂起函数采用CPS(Continuation Passing Style)和Switch状态机。能够保证每次挂起之后,后面的代码会在挂起函数执行完再继续执行。CPS是一种函数不直接返回值的代码风格。在这种风格中,函数将结果传入一个延续(Continuation,指"之后的内容"),后者决定了之后的逻辑

delay

delay()是常见的挂起函数,类似于线程的sleep()函数(运行在该线程下的所有协程都会被阻塞),但delay()不会阻塞线程

在这里插入图片描述
在这里插入图片描述

上面的代码中,在GlobalScope中启动了一个新的协程,意味着新协程的生命周期只受整个应用程序的生命周期限制。协程创建之后就会立即打印3,之后挂起,一秒后恢复之前的协程并打印4.而sleep一开始阻塞了主线程2秒,等主线程唤醒后立刻打印5.

yield

yield()用于挂起当前的协程,将当前的协程分发到CoroutineDispatcher的队列等其他协程完成/挂起之后,再继续执行先前的协程

在这里插入图片描述
在这里插入图片描述

withContext

withContext不会创建新的协程。withContext类似runBlocking,它的最后一行的值即为withContext的返回值,而且会阻塞上下文线程,通过Dispathcers来指定代码块运行的线程

在这里插入图片描述

withContext不像launch、async的context参数都是默认参数,并且使用默认值EmptyCoroutineContext。

withContext的context参数必须传值,它是CoroutineContext类型的。在使用withContext时,可以使用不同的CoroutineDispatcher,例如Dispatchers.Default(取代原先的CommonPool,kotlin1.3之后CommonPool变成了internal object)。
因为CoroutineDispatcher实现了CoroutineContext接口,所以这里传递CoroutineDispatcher

Dispatchers提供了CoroutineDispatcher的多种实现,有点类似于RxJava的Schedulers

Dispatchers提供的CoroutineDispatcher

Default表示使用后台线程的公共线程池
IO适用于I/O密集型操作的线程池
Unconfined表示在被调用的线程中启动协程,直到程序运行到第一个挂起点,协程会在相应的挂起函数所使用的任何线程中恢复

此外,常见的CoroutineDispatcher还可以通过ThreadPoolDispatcher的newSingleThreadContext()newFixedThreadPoolContext()来创建,以及Executor的扩展函数asCoroutineDispatcher()来创建。在Android中经常会用到Diapatchers.Main,它同样继承自CoroutinueDispatcher。使用之后可以在Android主线程上调度执行

在这里插入图片描述

withContext在使用NonCancellable时,能够让协程的任务执行完,即使会被调用者取消

在这里插入图片描述

coroutineScope

跟withContext类似,coroutineScope也会有返回值,但是coroutineScope采用父协程的CoroutineContext,无法使用其他的CoroutineDispatcher

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值