前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Kotlin 协程】协程启动 ④ ( 协程启动模式 | 协程构建器启动模式参数 | DEFAULT 模式 | ATOMIC 模式 | LAZY 模式 | UNDISPATCHED 模式 )

【Kotlin 协程】协程启动 ④ ( 协程启动模式 | 协程构建器启动模式参数 | DEFAULT 模式 | ATOMIC 模式 | LAZY 模式 | UNDISPATCHED 模式 )

作者头像
韩曙亮
发布2023-03-30 18:07:13
9720
发布2023-03-30 18:07:13
举报

文章目录

一、协程构建器 CoroutineScope.async 函数


1、协程构建器 CoroutineScope.async 函数参数分析

协程构建器 CoroutineScope.async 函数中 ,

代码语言:javascript
复制
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 函数原型 : 机翻文档 , 仅供参考 ;

代码语言:javascript
复制
// --------------- 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 模式的协程可以被取消 ;

代码语言:javascript
复制
runBlocking {
    // 调用 runBlocking 函数 , 可以将 主线程 包装成 协程
    
    // 指定协程的启动模式为 CoroutineStart.DEFAULT
    // 默认的 协程启动模式 , 协程创建后 , 马上开始调度执行 ,
    // 如果在调度前取消协程 , 则进入 取消响应 状态 ;
    val job = launch (start = CoroutineStart.DEFAULT) {
        Log.i(TAG, "协程开始执行")
        delay(2000)
        Log.i(TAG, "协程执行完毕")
    }
    delay(1000)
    // 取消协程
    job.cancel()
}

执行结果 :

代码语言:javascript
复制
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) 函数 时 , 协程才会取消 ;

代码语言:javascript
复制
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() 取消协程 , 则协程直接取消 ;

代码语言:javascript
复制
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 , 则 立刻在当前的主线程中执行协程 , 协程创建后 , 立即在当前的 函数调用栈 执行协程任务 , 打印当前线程时 会打印主线程 ;

代码语言:javascript
复制
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 中定义的协程启动模式原型

机翻文档 , 仅供参考 ;

代码语言:javascript
复制
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
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-11-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目录
  • 一、协程构建器 CoroutineScope.async 函数
    • 1、协程构建器 CoroutineScope.async 函数参数分析
      • 2、协程构建器 CoroutineScope.async 函数参数原型
      • 二、协程启动模式
        • 1、DEFAULT 模式
          • 2、ATOMIC 模式
            • 3、LAZY 模式
              • 4、UNDISPATCHED 模式
                • 5、CoroutineStart 中定义的协程启动模式原型
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档