From c160611e89d374240436755b078bdf8cad0d820e Mon Sep 17 00:00:00 2001 From: xiaoyan Date: Thu, 2 Mar 2023 18:08:00 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- language/kotlin/Kotlin协程.md | 66 +++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/language/kotlin/Kotlin协程.md b/language/kotlin/Kotlin协程.md index cc8fbb0..49841c0 100644 --- a/language/kotlin/Kotlin协程.md +++ b/language/kotlin/Kotlin协程.md @@ -37,14 +37,74 @@ 如上面代码所示,Kotlin中创建启动协程主要有3种方式: 1. runBlocking - 启动一个阻塞式协程,协程体代码块内代码会阻塞所在线程,返回值为泛型T,就是在协程体内最后一行返回的数据类型 2. launch - 启动一个非阻塞式协程,必须在协程作用域(CoroutineScope)内启动,但不会阻塞所在线程,返回值为Job -3. async - 启动一个非阻塞式协程,必须在协程作用域(CoroutineScope)内启动,但不会阻塞所在线程,返回值为一个Deferred,T泛型为协程体内最后一行返回的数据类型 +3. async - 启动一个非阻塞式协程,必须在协程作用域(CoroutineScope)内启动,但不会阻塞所在线程,返回值为一个Deferred,T泛型为协程体内最后一行返回的数据类型。 - 协程的返回类型 1. runBlocking的泛型返回值T runBlocking在默认情况下类似于一个封装好的方法,当然可以通过指定该方法运行的线程,让这个方法不阻塞主线程,这样就类似于我们在Java中的Thread,并且将run方法执行的结果最终返回回来,这里不做过多讨论。 -2. launch的返回类型Job -3. async的返回类型Deferred +2. launch的返回类型Job + + Job我们可以认为他就是一个协程作业是通过CoroutineScope.launch生成的,同时它运行一个指定的代码块,并在该代码块完成时完成。我们可以通过isActive、isCompleted、isCancelled来获取到Job的当前状态 + + 我们可以通过Job获取当前协程执行的状态,如下表所示,协程也存在对应的生命周期。 + + | State | [isActive] | [isCompleted] | [isCancelled] | + | ----- | :--------: | :-----------: | :----------: | + | New (optional initial state) | false | false | false | + | Active (default initial state)| true | false | false | + | Completing (transient state) | true | false | false | + | Cancelling (transient state) | false| false | true | + | Cancelled (final state) | false | true | true | + | Completed (final state) | false |true| false | + +3. async的返回类型Deferred + Deferred是继承自Job的,我们可以通过Deferred.await()方法获取协程的泛型T返回值。 + +## 0x12 详解协程参数 +上面的三种创建启动协程的方法,其构造方法内的参数都很类似。 +```Kotlin +public fun runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T + +public fun CoroutineScope.launch( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +): Job + +public fun CoroutineScope.async( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> T +): +``` +可以看到,runBlocking相比其他两个方法,缺少了协程启动模式CoroutineStart类型的参数,三个方法参数都有协程下上文CoroutineContext和协程执行体CoroutineScope.()。接下来我们就主要了解这三个参数类型。 + +- 协程上下文CoroutineContext + 它是一个包含了用户定义的一些各种不同元素的Element对象集合。其中主要元素是Job、协程调度器CoroutineDispatcher、还有包含协程异常CoroutineExceptionHandler、拦截器ContinuationInterceptor、协程名CoroutineName等。这些数据都是和协程密切相关的,每一个Element都一个唯一key。 + + 通过协程上下文,我们可以定义协程调度器、协程名称、协程的异常处理方式等等,而这些都可以通过协程上下文中的重载关键字来实现。 + +- 协程调度器CoroutineDispatcher + 如上文所述,协程调度器也实现了协程上下文的接口,协程调度器可以指定当前协程所在的线程。 + + Kotlin默认提供了4中类型的协程调度线程: + - Default:默认调度器,CPU密集型任务调度器,适合处理后台计算。通常处理一些单纯的计算任务,或者执行时间较短任务。比如:Json的解析,数据计算等。 + - IO:IO调度器,,IO密集型任务调度器,适合执行IO相关操作。比如:网络处理,数据库操作,文件操作等。 + - Main:UI调度器, 即在主线程上执行,通常用于UI交互,刷新等。 + - Unconfined:非受限调度器,又或者称为“无所谓”调度器,不要求协程执行在特定线程上。 + + 不指定调度器时,协程运行在Default默认调度器下,需要注意的是,Default调度器对应的线程并非为主线程。 + +- 协程启动模式 + CoroutineStart协程启动模式,是启动协程时需要传入的第二个参数。协程启动有4种 + - DEFAULT 默认启动模式,我们可以称之为饿汉启动模式,因为协程创建后立即开始调度,虽然是立即调度,单不是立即执行,有可能在执行前被取消。 + - LAZY 懒汉启动模式,启动后并不会有任何调度行为,直到我们需要它执行的时候才会产生调度。也就是说只有我们主动的调用Job的start、join或者await等函数时才会开始调度。 + - ATOMIC 一样也是在协程创建后立即开始调度,但是它和DEFAULT模式有一点不一样,通过ATOMIC模式启动的协程执行到第一个挂起点之前是不响应cancel 取消操作的,ATOMIC一定要涉及到协程挂起后cancel 取消操作的时候才有意义。 + - UNDISPATCHED 协程在这种模式下会直接开始在当前线程下执行,直到运行到第一个挂起点。这听起来有点像 ATOMIC,不同之处在于UNDISPATCHED是不经过任何调度器就开始执行的。当然遇到挂起点之后的执行,将取决于挂起点本身的逻辑和协程上下文中的调度器。 + + 这里需要特别注意的是UNDISPATCHED,非调度启动模式,在这个模式下,协程会在父协程所在线程直接执行,直到挂起函数出现,如果指定了对应的调度器,则会切换到对应的线程执行。 +- \ No newline at end of file