Kotlin 协程协程启动 ④ ( 协程启动模式 | 协程构建器启动模式参数 | DEFAULT 模式 | ATOMIC 模式 | LAZY 模式 | UNDISPATCHED 模式 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 协程协程启动 ④ ( 协程启动模式 | 协程构建器启动模式参数 | DEFAULT 模式 | ATOMIC 模式 | LAZY 模式 | UNDISPATCHED 模式 )相关的知识,希望对你有一定的参考价值。
文章目录
一、协程构建器 CoroutineScope.async 函数
1、协程构建器 CoroutineScope.async 函数参数分析
协程构建器 CoroutineScope.async 函数中 ,
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
- 第一个参数
context: CoroutineContext = EmptyCoroutineContext
是协程的上下文对象 ; - 第二个参数
start: CoroutineStart = CoroutineStart.DEFAULT
是协程的启动模式 ; - 第三个参数
block: suspend CoroutineScope.() -> T
是协程作用域代码块 , 其中是协程任务代码 ;
2、协程构建器 CoroutineScope.async 函数参数原型
CoroutineScope.async 函数原型 : 机翻文档 , 仅供参考 ;
// --------------- async ---------------
/**
* 创建协程并将其未来结果作为[Deferred]的实现返回。
* 当产生的延迟为[cancelled][Job.cancel]时,正在运行的协程将被取消。
* 得到的协程与其他语言中的类似原语相比有一个关键的区别
* 和框架:它取消父作业(或外部作用域)在执行*结构化并发*范式失败。
* 要改变这种行为,可以使用监督父级([SupervisorJob]或[supervisor orscope])。
*
* 协程上下文从[CoroutineScope]继承,附加的上下文元素可以用[context]参数指定。
* 如果上下文没有任何dispatcher,也没有任何其他[ContinuationInterceptor],则[Dispatchers.]默认使用“Default”。
* 父作业也从[CoroutineScope]继承,但它也可以被覆盖
* 使用相应的[上下文]元素。
*
* 默认情况下,协程是立即调度执行的。
* 其他选项可以通过' start '参数指定。参见[coroutinstart]了解详细信息。
* 可选参数[start]可以设置为[coroutinstart]。启动协同程序。在这种情况下,
* 结果[Deferred]是在_new_状态下创建的。它可以显式地以[start][Job.start]开始
* 函数,并将在第一次调用[join][Job时隐式启动。加入],[等待][递延。await]或[awaitAll]。
*
* @param block 协同程序代码。
*/
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T>
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
二、协程启动模式
协程启动模式 :
- DEFAULT 模式 : 默认的 协程启动模式 , 协程创建后 , 马上开始调度执行 , 如果在 执行前或执行时 取消协程 , 则进入 取消响应 状态 ;
1、DEFAULT 模式
DEFAULT 模式 : 默认的 协程启动模式 , 协程创建后 , 马上开始调度执行 , 如果在 执行前或执行时 取消协程 , 则进入 取消响应 状态 ; 如果在执行过程中取消 , 协程也会被取消 ;
代码示例 : DEFAULT 模式的协程可以被取消 ;
runBlocking
// 调用 runBlocking 函数 , 可以将 主线程 包装成 协程
// 指定协程的启动模式为 CoroutineStart.DEFAULT
// 默认的 协程启动模式 , 协程创建后 , 马上开始调度执行 ,
// 如果在调度前取消协程 , 则进入 取消响应 状态 ;
val job = launch (start = CoroutineStart.DEFAULT)
Log.i(TAG, "协程开始执行")
delay(2000)
Log.i(TAG, "协程执行完毕")
delay(1000)
// 取消协程
job.cancel()
执行结果 :
00:44:46.329 I 协程开始执行
00:44:48.372 I 协程执行完毕
如果协程没有执行完 , 就取消协程任务 , 则协程会被中途取消 ;
协程是 基于线程 的 , 线程 由 调度器 控制 , 线程包含主线程和子线程 ;
上述代码中 , 调用 runBlocking 函数 , 可以将 主线程 包装成 协程 , launch 启动协程 , 该协程运行在主线程中 ,
运行到 delay(2000)
代码时 , 该 delay 函数是挂起函数 , 主线程会被挂起 , 主线程被调度器调度 , 执行其它的操作 如 刷新 UI 等操作 , 挂起函数中的内容会在子线程中执行 ,
如果 launch 启动协程时 , 此时会被调度器 立即调度 , 但是 主线程不会立即执行 , 如 主线程正在执行 刷新 UI 等任务 , 此时如果取消该协程 , 则协程直接取消 ;
如果在主线程中执行协程 , 协程挂起后 , 主线程继续执行其它任务, 如刷新 UI 等 , 主线程不会阻塞 , 挂起函数会在子线程中执行 ;
一般会将耗时操作放在 协程的挂起函数 中执行 ;
2、ATOMIC 模式
ATOMIC 模式 : 协程创建后 , 马上开始调度执行 , 协程执行到 第一个挂起点 之前 , 如果取消协程 , 则不进行响应取消操作 ;
代码示例 : 在下面的代码中 , 协程执行后 , 遇到的 第一个挂起函数是 delay(2000)
函数 , 该 挂起函数之前的代码执行过程中 , 如果取消协程 , 则该 协程不会取消 , 直到执行到 第一个挂起函数是 delay(2000)
函数 时 , 协程才会取消 ;
runBlocking
// 调用 runBlocking 函数 , 可以将 主线程 包装成 协程
// 指定协程的启动模式为 CoroutineStart.ATOMIC
// 协程创建后 , 马上开始调度执行 ,
// 协程执行到 第一个挂起点 之前 , 如果取消协程 , 则不进行响应取消操作 ;
val job = launch (start = CoroutineStart.ATOMIC)
Log.i(TAG, "协程开始执行")
delay(2000)
Log.i(TAG, "协程执行完毕")
// 取消协程
job.cancel()
3、LAZY 模式
ATOMIC 模式 : 协程创建后 , 不会马上开始调度执行 , 只有 主动调用协程的 start , join , await 方法 时 , 才开始调度执行协程 , 如果在 调度之前取消协程 , 该协程直接报异常 进入异常响应状态 ;
代码示例 : 在下面的代码中 , val job = async (start = CoroutineStart.LAZY)
只是定义协程 , 并不会马上执行 , 在执行 job.start()
或 job.await()
代码时 , 才开始调度执行协程 , 如果在这之前调用 job.cancel()
取消协程 , 则协程直接取消 ;
runBlocking
// 调用 runBlocking 函数 , 可以将 主线程 包装成 协程
// 指定协程的启动模式为 CoroutineStart.LAZY
// 协程创建后 , 不会马上开始调度执行 ,
// 只有 主动调用协程的 start , join , await 方法 时 , 才开始调度执行协程 ,
// 如果在 调度之前取消协程 , 该协程直接报异常 进入异常响应状态 ;
val job = async (start = CoroutineStart.LAZY)
Log.i(TAG, "协程开始执行")
delay(2000)
Log.i(TAG, "协程执行完毕")
"Hello" // 返回一个字符串
delay(1000)
// 取消协程 , 在调度之前取消 , 协程直接进入异常响应状态
//job.cancel()
// 执行下面两个方法中的任意一个方法 ,
// 启动执行协程
job.start()
// 获取协程返回值
job.await()
4、UNDISPATCHED 模式
UNDISPATCHED 模式 : 协程创建后 , 立即在当前的 函数调用栈 执行协程任务 , 直到遇到第一个挂起函数 , 才在子线程中执行挂起函数 ;
- 如果在主线程中启动协程 , 则该模式的协程就会直接在主线程中执行 ;
- 如果在子线程中启动协程 , 则该模式的协程就会直接在子线程中执行 ;
代码示例 : Dispatchers.IO 调度器是将协程调度到子线程执行 , 但是如果 协程启动模式为 UNDISPATCHED , 则 立刻在当前的主线程中执行协程 , 协程创建后 , 立即在当前的 函数调用栈 执行协程任务 , 打印当前线程时 会打印主线程 ;
runBlocking
// 调用 runBlocking 函数 , 可以将 主线程 包装成 协程
// 指定协程的启动模式为 CoroutineStart.UNDISPATCHED
// 协程创建后 , 立即在当前的 函数调用栈 执行协程任务 ,
// 直到遇到第一个挂起函数 , 才在子线程中执行挂起函数 ;
val job = async ( context = Dispatchers.IO, start = CoroutineStart.UNDISPATCHED )
// Dispatchers.IO 调度器是将协程调度到子线程执行
// 但是如果 协程启动模式为 UNDISPATCHED , 则立刻在当前的主线程中执行协程
// 此时打印出主线程 ,
// 协程创建后 , 立即在当前的 函数调用栈 执行协程任务 , 因此会打印主线程
Log.i(TAG, "协程开始执行 当前线程 : $Thread.currentThread()")
// 遇到挂起函数会在子线程执行该挂起函数
// 挂起函数都是耗时任务
delay(2000)
Log.i(TAG, "协程执行完毕")
"Hello" // 返回一个字符串
5、CoroutineStart 中定义的协程启动模式原型
机翻文档 , 仅供参考 ;
package kotlinx.coroutines
import kotlinx.coroutines.CoroutineStart.*
/**
* 定义协同程序构建器的开始选项。
* 它用于[launch][CoroutineScope的' start '参数中。发射],[异步][CoroutineScope。以及其他协程构建器函数。
*
* 协程启动选项的汇总如下:
* * [DEFAULT]——根据上下文立即安排协程执行;
* * [LAZY]—只在需要时才启动协程;
* * [ATOMIC]——原子地(以不可取消的方式)根据上下文安排协程执行;
* * [UNDISPATCH]——立即执行协程,直到它在当前线程中的第一个挂起点_。
*/
public enum class CoroutineStart
/**
* Default——根据上下文立即安排协程执行。
*
* 如果协程上下文的[CoroutineDispatcher]从[CoroutineDispatcher. isdispatchneeded]返回' true '
* 像大多数调度程序那样运行,那么协程代码稍后被调度执行,而代码则被调度执行
* 调用的协程构建器继续执行。
*
* 注意[Dispatchers.]总是从它的[CoroutineDispatcher.isDispatchNeeded]返回' false '
* 函数,因此启动与[Dispatchers.]的协程。使用[DEFAULT]与使用[undispatch]相同。
*
* 如果协程[Job]在它甚至有机会开始执行之前被取消,那么它将不会启动它的
* 执行,但将以异常完成。
*
* 协程在挂起点的可取消性取决于的具体实现细节
* 暂停功能。使用[suspendCancellableCoroutine]实现可取消的挂起函数。
*/
DEFAULT,
/**
* 只有在需要时才会惰性地启动协程。
*
* 有关详细信息,请参阅相应协程构建器的文档
* (如[发射][CoroutineScope。和[async][CoroutineScope.async])。
*
* 如果协程[Job]在它甚至有机会开始执行之前被取消,那么它将不会启动它的
* 执行,但将以异常完成。
*/
LAZY,
/**
* 原子地(即,以一种不可取消的方式)根据上下文安排协程的执行。
* 这类似于[DEFAULT],但是协程在开始执行之前不能被取消。
*
* 协程在挂起点上的可取消性取决于的具体实现细节
* suspend功能如[DEFAULT]。
*/
@ExperimentalCoroutinesApi // Since 1.0.0, no ETA on stability
ATOMIC,
/**
* 立即执行协程,直到它在当前线程中的第一个挂起点_
* 正在使用[dispatchers . unrestricted]启动协程。但是,当从挂起恢复协程时
* 它根据上下文中的[CoroutineDispatcher]进行分派。
*
* 这与[ATOMIC]在某种意义上类似,协程开始执行,即使它已经被取消,
* 但不同的是,它在同一个线程中开始执行。
*
* 协程在挂起点上的可取消性取决于的具体实现细节
* suspend功能如[DEFAULT]。
*
* 无限制事件循环
*
* 与调度程序。和[MainCoroutineDispatcher。],嵌套的未分派协程不会形成
* 在无限制嵌套的情况下防止潜在堆栈溢出的事件循环。
*/
UNDISPATCHED;
/**
* 当[LAZY]时返回' true '。
*
* @suppress **这是一个内部API,不应该从通用代码中使用
*/
@InternalCoroutinesApi
public val isLazy: Boolean get() = this === LAZY
以上是关于Kotlin 协程协程启动 ④ ( 协程启动模式 | 协程构建器启动模式参数 | DEFAULT 模式 | ATOMIC 模式 | LAZY 模式 | UNDISPATCHED 模式 )的主要内容,如果未能解决你的问题,请参考以下文章
深入理解Kotlin协程协程作用域启动模式调度器异常和取消使用篇
Kotlin 协程协程启动 ⑤ ( 协程作用域构建器 | runBlocking 函数 | coroutineScope 函数 | supervisorScope 函数 )
Kotlin 协程协程启动 ⑤ ( 协程作用域构建器 | runBlocking 函数 | coroutineScope 函数 | supervisorScope 函数 )
Kotlin 协程协程启动 ① ( 协程构建器 | launch 构建器 | async 构建器 | runBlocking 函数 | Deferred 类 )
Kotlin 协程协程启动 ① ( 协程构建器 | launch 构建器 | async 构建器 | runBlocking 函数 | Deferred 类 )