前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Kotlin协程-一个协程的生命周期

Kotlin协程-一个协程的生命周期

作者头像
PhoenixZheng
发布于 2021-04-26 04:27:06
发布于 2021-04-26 04:27:06
1K00
代码可运行
举报
运行总次数:0
代码可运行

在安卓或者kotlin平台上使用协程是很简单的一件事情。举一个最简单的例子,不依赖安卓平台的协程代码,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fun main() {
    GlobalScope.launch {
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    delay(100L)
    println("Hello,") // 协程已在等待时主线程还在继续

    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
    println("out launch done")
}

这里启动了一个常见的coroutine,GlobalScope.launch启动的协程在协程上下文中会派发给底层的线程池去执行。它会经历创建->拦截->暂停->resume->暂停->resume—>完成的生命周期。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
flowchat
st=>start: 创建
p=>operation: 暂停
r=>operation: 继续
e=>end: 结束
con=>condition: 完成?

st->p->r->con
con(yes)->e
con(no)->p

协程的生命周期是在一系列的逻辑中实现的,背后是 Context-Dispatcher-Scheduler 的支持。这些代码没有很深的技术,用的都是常见的软件设计思想,梳理这部分逻辑大概用了两天时间,过程中主要需要保持两条清晰的线索,一个是协程的生命周期,一个是生命周期背后支撑的逻辑概念。

创建协程

launch/async 协程的创建有两个常用接口launch和async,两个接口的内部实现基本一致。以launch来说,它的源码在 Builders.common.kt

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,//默认的立即启动方式
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)//创建context
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)//创建立即启动的coroutine
    coroutine.start(start, coroutine, block)
    return coroutine
}

launch会返回一个Job对象,Job提供了一种类似Future的实现,可以在协程运行完成后返回结果。

返回coroutine之前会调用 coroutine.start()方法,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
coroutine.start(start, coroutine, block)

这行代码会把协程加入到队列中。代码调用的是 AbstractCoroutine.kt的 start方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
    initParentJob()
    start(block, receiver, this)
}

从start(block, receiver, this)开始是派发流程。此时的coroutine已经拥有了协程上下文,和默认的派发器和调度器。

CoroutineStart是一个枚举类。start干了啥?为什么一个枚举类的值可以直接当函数使用?这是因为它使用了kotlin的语言特性--操作符重载,CoroutineStart枚举类的invoke方法被重载了,所以可以直接用 start 去执行代码。操作符重载的代码在 CoroutineStart 中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@InternalCoroutinesApi
public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>) =
    when (this) {
        CoroutineStart.DEFAULT -> block.startCoroutineCancellable(completion) //start实际执行的是这行代码
        CoroutineStart.ATOMIC -> block.startCoroutine(completion)
        CoroutineStart.UNDISPATCHED -> block.startCoroutineUndispatched(completion)
        CoroutineStart.LAZY -> Unit // will start lazily
    }

派发

经过创建的协程就进入了派发流程,Dispatcher会将它依据规则加入到对应队列里。关于Dispatcher等一下会再说是什么时候创建的什么东西,这里先记住有个Dispatcher就行。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
block.startCoroutineCancellable(completion)

从上面这行代码的startCoroutineCancellable跟进去来到 Cancellable.kt,它的代码很简单。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@InternalCoroutinesApi
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>) = runSafely(completion) {
    createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith(Result.success(Unit))
}

createCoroutineUnintercepted是一个expect函数,意味着它会有一个actual的实现。但我们在kotlinx的代码中是找不到actual实现的,它的actual实现在Kotlin中,后面我们会分析这块代码。

现在只要记住createCoroutineUnintercepted,最终会调用下面这个create接口就行

上面的代码哪里来的?

我们写的协程代码,会经过kotlinc的编译,而这些代码就是在编译期插入的。

createCoroutineUnintercepted调用了create接口后,会得到一个 Continuation 的实现。在开篇说过,Continuation是一个带resumeWith()的接口,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Continuation<in T> {
    public val context: CoroutineContext
    public fun resumeWith(result: Result<T>)
}

因此这里create之后返回的,实际是个ContinuationImpl实现。代码在 ContinuationImpl.kt中,ContinuationImpl比较特殊,它不在kotlinx项目里,而在kotlin-stdlib标准库。

kotlin的协程架构着实有点蛋疼,这种有些在标准库,有些在kotlinx里的方式让人捉摸不透。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@SinceKotlin("1.3")
// State machines for named suspend functions extend from this class
internal abstract class ContinuationImpl(
    completion: Continuation<Any?>?,
    private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
    ...

    public fun intercepted(): Continuation<Any?> =
        intercepted
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also { intercepted = it }

上面省略了一些代码。得到了ContinuationImpl实现后会调用它的intercepted()方法。重点是这行代码,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
context[ContinuationInterceptor]

这里会拿到当前上下文Context中的派发器对象,默认的实现是CoroutineDispatcher。这个东西是哪里来的,回到最上面的 launch接口,第一行是 newCoroutineContext,就是从这里来的。

接着在 CoroutineDispatcher 中,会调用 interceptContinuation() 方法返回一个DispatchedContinuation对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
        DispatchedContinuation(this, continuation)

顾名思义,DispatchedContinuation代表现在的coroutine,不仅实现了continuation接口,同时还通过代理的方式持有了Dispatcher。

再接着看Cancellable。intercept之后,我们的协程就处于拦截/暂停/挂起状态,在协程里的概念叫suspend。接着执行resumeCancellableWith()。

目前的corutine是 DispatchedContinuation,resumeCancellableWith的实现在它的代码中,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
inline fun resumeCancellableWith(result: Result<T>) {
    val state = result.toState()
    if (dispatcher.isDispatchNeeded(context)) {
        _state = state
        resumeMode = MODE_CANCELLABLE
        dispatcher.dispatch(context, this)//进入派发流程
    } else {
        executeUnconfined(state, MODE_CANCELLABLE) {
            if (!resumeCancelled()) {
                resumeUndispatchedWith(result)
            }
        }
    }
}

里面的dispatcher,是在创建的时候传入的 Dispatcher实现。这里通过代理模式,调用它的派发函数。之后就进入了派发流程。

kotlin协程的常用派发器有两个,EventLoop和DefaultScheduler,关于EventLoop我们后面会讲,它比较特殊,因为它的设计是为了阻塞当前线程,完成一系列coroutine。

DefaultScheduler的实现在 Dispatcher.kt。相关的类还有 Dispatchers.common.kt,Dispatchers.kt。他们之间的关系是 Dispatches.common.kt是公用类,它指导了所有平台的协程需要实现的公共接口。而不同的平台,比如jvm,js,native,他们的具体实现都叫Disaptchers.kt,分别放在不同的包下面。

Dispatches(多了个s)定义了几种派发类型,之前说过,Default,MAIN,Unconfine,IO。我们关注的是Default,其他三个的逻辑可以参考Default的实现。

Dispatcher的创建时机在 newCoroutineContext(),也就是launch的第一行。它的实现在 CoroutineContext.kt里(jvm包下),

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
internal actual fun createDefaultDispatcher(): CoroutineDispatcher =
    if (useCoroutinesScheduler) DefaultScheduler else CommonPool

/**
 * Creates context for the new coroutine. It installs [Dispatchers.Default] when no other dispatcher nor
 * [ContinuationInterceptor] is specified, and adds optional support for debugging facilities (when turned on).
 *
 * See [DEBUG_PROPERTY_NAME] for description of debugging facilities on JVM.
 */
@ExperimentalCoroutinesApi
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext):  CoroutineContext { //创建context
    val combined = coroutineContext + context
    val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
    return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
        debug + Dispatchers.Default else debug
}

创建context的时候会用到 Dispatchers.Default,最终它会回去调用上面那句createDefaultDispatcher()。从而拿到 DefaultScheduler 单例。

jvm平台的Dispatcher.Default是这样的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()

createDefaultDispatcher()的实现刚刚上面介绍了。

然后进去Dispatcher看,在CoroutineContinuation调用了disaptcher.dispatch(),调用的是哪个函数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
override fun dispatch(context: CoroutineContext, block: Runnable): Unit =
    try {
        coroutineScheduler.dispatch(block)
    } catch (e: RejectedExecutionException) {
        DefaultExecutor.dispatch(context, block)
    }

coroutineScheduler就是下面要说到的调度器了。现在coroutine还处于suspend状态,接下来就要进入调度逻辑了。

调度

默认的调度实现是 CoroutineScheduler,在CoroutineScheduler.kt下。它的diaptch()函数,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fun dispatch(block: Runnable, taskContext: TaskContext = NonBlockingContext, tailDispatch: Boolean = false) {
    trackTask() // this is needed for virtual time support
    val task = createTask(block, taskContext) //封装任务
    // try to submit the task to the local queue and act depending on the result
    val currentWorker = currentWorker() //获取当前线程
    val notAdded = currentWorker.submitToLocalQueue(task, tailDispatch) //加入worker本地执行队列
    if (notAdded != null) {
        if (!addToGlobalQueue(notAdded)) {//加入全局队列
            // Global queue is closed in the last step of close/shutdown -- no more tasks should be accepted
            throw RejectedExecutionException("$schedulerName was terminated")
        }
    }
    val skipUnpark = tailDispatch && currentWorker != null
    // Checking 'task' instead of 'notAdded' is completely okay
    if (task.mode == TASK_NON_BLOCKING) {
        if (skipUnpark) return
        signalCpuWork() //执行CPU密集型协程
    } else {
        // Increment blocking tasks anyway
        signalBlockingWork(skipUnpark = skipUnpark) //执行阻塞型协程
    }
}

在调度器里面有两个新的概念,Worker和Queue。所谓Worker其实就是Thread,跟java的Thread是同一个东西。Queue是任务队列,它又分两种队列,一个是Worker内部的localQueue,一个是Scheduler里的globalQueue。虽然 globalQueue 又分 blocking 和 cpu,但这里可以简单理解为 globalQueue里面放的是阻塞型IO任务。

回到Worker,它有个内部成员 localQueue,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
internal inner class Worker private constructor() : Thread() {
    init {
        isDaemon = true
    }

    // guarded by scheduler lock, index in workers array, 0 when not in array (terminated)
    @Volatile // volatile for push/pop operation into parkedWorkersStack
    var indexInArray = 0
        set(index) {
            name = "$schedulerName-worker-${if (index == 0) "TERMINATED" else index.toString()}"
            field = index
        }

    constructor(index: Int) : this() {
        indexInArray = index
    }

    inline val scheduler get() = this@CoroutineScheduler

    @JvmField
    val localQueue: WorkQueue = WorkQueue() //本地队列

localQueue是存在于每个worker的,也就是说,不管开了多少个线程,每个线程都持有一个属于自己的队列。Worker在创建完毕之后就进入运行状态,直到它的状态被设置为销毁为止。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private fun createNewWorker(): Int {
    synchronized(workers) {
        ...
        val worker = Worker(newIndex)
        workers[newIndex] = worker
        require(newIndex == incrementCreatedWorkers())
        worker.start()
        return cpuWorkers + 1
    }
}

省略了部分代码。在创建完worker之后,对象会加入到一个数组里,这个数组属于调度器。之后就会调用start()方法了。worker会看是否有可以执行的任务,有的话就取出来做,没有的话就进入park状态。park是线程调度里一个不是很常见的概念,这部分可以再仔细研究。

下面是执行部分的逻辑。

执行

在Worker的run()函数会调用runWorker()函数,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private fun runWorker() {
    var rescanned = false
    while (!isTerminated && state != WorkerState.TERMINATED) {
        val task = findTask(mayHaveLocalTasks)
        // Task found. Execute and repeat
        if (task != null) {
            rescanned = false
            minDelayUntilStealableTaskNs = 0L
            executeTask(task) //执行

跳到 executeTask(),

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private fun executeTask(task: Task) {
    val taskMode = task.mode
    idleReset(taskMode)
    beforeTask(taskMode)
    runSafely(task)
    afterTask(taskMode)
}

idleReset,beforeTask和afterTask做的是一些状态设置和回调。主要的执行是 runSafely(),

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fun runSafely(task: Task) {
    try {
        task.run() //真正的执行
    } catch (e: Throwable) {
        val thread = Thread.currentThread()
        thread.uncaughtExceptionHandler.uncaughtException(thread, e)
    } finally {
        unTrackTask()
    }
}

task是个啥?之前在intercept()返回的DispatchedContinuation,它继承了 DispatchedTask(),这里的task就是它了。在 DispatchedTask.kt里,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
internal abstract class DispatchedTask<in T>(
    @JvmField public var resumeMode: Int
) : SchedulerTask() {
    internal abstract val delegate: Continuation<T>

    internal abstract fun takeState(): Any?

    internal open fun cancelResult(state: Any?, cause: Throwable) {}

    @Suppress("UNCHECKED_CAST")
    internal open fun <T> getSuccessfulResult(state: Any?): T =
        state as T

    internal fun getExceptionalResult(state: Any?): Throwable? =
        (state as? CompletedExceptionally)?.cause

    public final override fun run() {
        val taskContext = this.taskContext
        var fatalException: Throwable? = null
        try {
            val delegate = delegate as DispatchedContinuation<T>
            val continuation = delegate.continuation
            val context = continuation.context
            val state = takeState() // NOTE: Must take state in any case, even if cancelled
            withCoroutineContext(context, delegate.countOrElement) {
                val exception = getExceptionalResult(state)
                val job = if (resumeMode.isCancellableMode) context[Job] else null
                /*
                 * Check whether continuation was originally resumed with an exception.
                 * If so, it dominates cancellation, otherwise the original exception
                 * will be silently lost.
                 */
                if (exception == null && job != null && !job.isActive) {
                    val cause = job.getCancellationException()
                    cancelResult(state, cause)
                    continuation.resumeWithStackTrace(cause)
                } else {
                    if (exception != null) continuation.resumeWithException(exception)
                    else continuation.resume(getSuccessfulResult(state)) //调用continuation的resume
                }
            }

最后一行是调用continuation的地方。这里的continuation又是在最开始创建DispatchedContinuation那里传进来的。它实际是个 BaseContinuationImpl 对象,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
internal abstract class BaseContinuationImpl(
    // This is `public val` so that it is private on JVM and cannot be modified by untrusted code, yet
    // it has a public getter (since even untrusted code is allowed to inspect its call stack).
    public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
    // This implementation is final. This fact is used to unroll resumeWith recursion.
    public final override fun resumeWith(result: Result<Any?>) {
        // This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
        var current = this
        var param = result
        while (true) {
            // Invoke "resume" debug probe on every resumed continuation, so that a debugging library infrastructure
            // can precisely track what part of suspended callstack was already resumed
            probeCoroutineResumed(current)
            with(current) {
                val completion = completion!! // fail fast when trying to resume continuation without completion
                val outcome: Result<Any?> =
                    try {
                        val outcome = invokeSuspend(param) //真正调用我们写的代码的地方
                        if (outcome === COROUTINE_SUSPENDED) return
                        Result.success(outcome)
                    } catch (exception: Throwable) {
                        Result.failure(exception)
                    }
                releaseIntercepted() // this state machine instance is terminating
                if (completion is BaseContinuationImpl) {
                    // unrolling recursion via loop
                    current = completion
                    param = outcome
                } else {
                    // top-level completion reached -- invoke and return
                    completion.resumeWith(outcome)
                    return
                }
            }
        }
    }

上面的 invokeSuspend()才是真正调用我们写的协程的地方。到这里就是真正的执行流程了。

整个流程下来非常绕,有些代码在标准库,而有些又在协程库,山路十八弯。

invokeSuspend()是在编译期插入的,比如下面这段代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
fun main() {
    GlobalScope.launch {
        println("Hello!")
        delay(100L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    Thread.sleep(400L) // 阻塞主线程 2 秒钟来保证 JVM 存活
    println("out launch done")
}

非常简单,只起了一个协程的情况。在编译后会变成下面这样

它实际是个状态机,每次挂起和resume都会发生状态切换,根据状态执行不同的case。

结束

协程结束的时机是在coroutine返回的不是 COROUTINE_SUSPENDED 的时候。invokeSuspend的case中,遇到挂起函数会返回COROUTINE_SUSPENDED,而在ContinuationImpl中收到它则直接返回。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    public final override fun resumeWith(result: Result<Any?>) {
        // This loop unrolls recursion in current.resumeWith(param) to make saner and shorter stack traces on resume
        var current = this
        var param = result
        while (true) {
            // Invoke "resume" debug probe on every resumed continuation, so that a debugging library infrastructure
            // can precisely track what part of suspended callstack was already resumed
            probeCoroutineResumed(current)
            with(current) {
                val completion = completion!! // fail fast when trying to resume continuation without completion
                val outcome: Result<Any?> =
                    try {
                        val outcome = invokeSuspend(param)
                        if (outcome === COROUTINE_SUSPENDED) return //直接返回
                        Result.success(outcome)
                    } catch (exception: Throwable) {
                        Result.failure(exception)
                    }
                releaseIntercepted() // this state machine instance is terminating
                if (completion is BaseContinuationImpl) {
                    // unrolling recursion via loop
                    current = completion
                    param = outcome
                } else {
                    // top-level completion reached -- invoke and return
                    completion.resumeWith(outcome)
                    return
                }
            }
        }
    }

所以当最后一个case的时候,返回的是Unit.INSTANCE。此时协程就真正的地执行完毕了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android每日一讲 微信公众号,前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
抽丝剥茧Kotlin - 协程
文章接上篇,这一篇我们好好聊一聊协程的原理,通过上一篇的学习,相信大家对于如何使用协程已经非常熟悉了。
用户1907613
2020/08/28
8550
Kotlin协程-协程的内部概念Continuation
+-------+           +-----------+ | START |----------------------->| SUSPENDED | +-------+           +-----------+                  |  ^                  V  |                +------------+ completion invoked +-----------+                | RUNNING |------------------->| COMPLETED |                +------------+          +-----------+
PhoenixZheng
2021/05/17
1.8K0
Kotlin协程-协程的内部概念Continuation
Kotlin协程解析系列(上):协程调度与挂起
本文是Kotlin协程解析系列文章的开篇,主要介绍Kotlin协程的创建、协程调度与协程挂起相关的内容
2020labs小助手
2022/06/07
2.2K0
协程到底是怎么切换线程的?
可以看出CoroutineScope的代码很简单,主要作用是提供CoroutineContext,协程运行的上下文 我们常见的实现有GlobalScope,LifecycleScope,ViewModelScope等
Rouse
2021/07/30
9040
协程到底是怎么切换线程的?
Kotlin协程实现原理:ContinuationInterceptor&CoroutineDispatcher
新的一周开始了,大家继续干就完事了,同时感谢老铁们的支持! 今天我们继续来聊聊Kotlin的协程Coroutine。 如果你还没有接触过协程,推荐你先阅读这篇入门级文章What? 你还不知道Kotli
Rouse
2020/11/17
1.8K0
Kotlin协程实现原理:ContinuationInterceptor&CoroutineDispatcher
kotlin-协程的异常处理机制分析
使用kotlin的协程一段时间了,常用的用法也已经很熟悉,但都是停留在使用的阶段,没有对代码深入了解过,还是感觉有点虚;趁着过年这段时间,针对协程的异常处理,对其相关的源码学习了一波,梳理总结一下自己的理解。
37手游安卓团队
2021/02/22
9780
深入浅出Kotlin协程
协程(Coroutines)已经随着Kotlin1.3版本一起发布了1.0正式版,android平台可以使用如下方式引入:
wiizhang
2018/09/11
11.7K3
深入浅出Kotlin协程
揭秘kotlin协程中的CoroutineContext
从kotlin1.1开始,协程就被添加到kotlin中作为实验性功能,直到kotlin1.3,协程在kotlin中的api已经基本稳定下来了,现在kotlin已经发布到了1.4,为协程添加了更多的功能并进一步完善了它,所以我们现在在kotlin代码中可以放心的引入kotlin协程并使用它,其实协程并不是kotlin独有的功能,它是一个广泛的概念,协作式多任务的实现,除了kotlin外,很多语言如Go、Python等都通过自己的方式实现了协程,本文阅读前希望你已经知道如何使用kotlin协程,如果不熟悉可以阅读一下官方文档:
做个快乐的码农
2022/02/07
2K0
揭秘kotlin协程中的CoroutineContext
Kotlin协程实现原理:挂起与恢复
今天我们来聊聊Kotlin的协程Coroutine。 如果你还没有接触过协程,推荐你先阅读这篇入门级文章What? 你还不知道Kotlin Coroutine? 如果你已经接触过协程,但对协程的原理存
Rouse
2020/11/26
2.3K0
Android中的Coroutine协程原理详解
传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高。但是面对计算密集型的任务不如多线程并行运算效率高。
BlueSocks
2022/03/29
1K0
Android中的Coroutine协程原理详解
Kotlin 协程实现原理解析
Kotlin 协程是一种在 Kotlin 语言中实现并发编程的强大工具。它提供了一种轻量级的线程管理方式,使得开发者能够以接近同步代码的方式编写异步代码。本文将深入探讨 Kotlin 协程的实现原理,并分析其关键源码。
用户9505469
2024/04/10
6720
破解 Kotlin 协程(6) - 协程挂起篇
我们刚刚学线程的时候,最常见的模拟各种延时用的就是 Thread.sleep 了,而在协程里面,对应的就是 delay。 sleep 让线程进入休眠状态,直到指定时间之后某种信号或者条件到达,线程就尝试恢复执行,而 delay 会让协程挂起,这个过程并不会阻塞 CPU,甚至可以说从硬件使用效率上来讲是“什么都不耽误”,从这个意义上讲 delay 也可以是让协程休眠的一种很好的手段。
bennyhuo
2020/02/20
1.2K0
Kotlin协程-协程派发和调度框架
一个coroutine创建好之后,就交给协程框架去调度了。这篇主要讲从launch{...}开始,到最终得到执行的时候,所涉及到的协程框架内部概念。
PhoenixZheng
2021/04/26
1.1K0
Kotlin协程系列(二)
  在进行业务开发时,我们通常会基于官方的协程框架(kotlinx.coroutines)来运用Kotlin协程优化异步逻辑,不过这个框架过于庞大和复杂,如果直接接触它容易被劝退。所以,为了我们在后续的学习中游刃有余,在使用官方给出的复合协程时能够胸有成竹,我们暂且抛开它,按照它的思路实现一个轻量版的协程框架。
故乡的樱花开了
2023/11/28
2930
Kotlin协程-特殊的阻塞协程
阻塞协程是种特殊的协程启动方式,一般是用 runBlocking{} 扩起来一段协程。
PhoenixZheng
2021/05/17
2.5K0
破解 Kotlin 协程(3) - 协程调度篇
前面我们提到 launch 函数有三个参数,第一个参数叫 上下文,它的接口类型是 CoroutineContext,通常我们见到的上下文的类型是 CombinedContext 或者 EmptyCoroutineContext,一个表示上下文的组合,另一个表示什么都没有。我们来看下 CoroutineContext 的接口方法:
bennyhuo
2020/02/20
8090
kotlin--协程上下文、异常处理
当我们在a协程延迟函数100ms之前开启一个子协程b,b做了200ms的事情,如果不考虑调度消耗的时间,那么a协程的生命也会延长成200ms
aruba
2021/12/06
9770
kotlin--协程上下文、异常处理
协程切换引发ANR?Dispatcers.IO线程池饥饿的六种破解姿势
大家好,我是稳稳,一个曾经励志用技术改变世界,现在为失业做准备的中年奶爸程序员,与你分享生活和学习的点滴。
AntDream
2025/05/12
600
协程切换引发ANR?Dispatcers.IO线程池饥饿的六种破解姿势
【Kotlin 协程】协程异常处理 ④ ( Android 协程中出现异常导致应用崩溃 | Android 协程中使用协程异常处理器捕获异常 | Android 全局异常处理器 )
在前几篇博客示例中 , 协程中 如果出现异常 , 没有进行捕获 , 则程序直接崩溃 , 这种情况下需要进行 异常的捕获 以 避免 Android 应用程序的崩溃 ;
韩曙亮
2023/03/30
1.7K0
【Kotlin 协程】协程异常处理 ④ ( Android 协程中出现异常导致应用崩溃 | Android 协程中使用协程异常处理器捕获异常 | Android 全局异常处理器 )
Kotlin协程系列(一)
  最近看了一本有关kotlin协程的书籍,对协程又有了不一样的了解,所以准备写一个关于kotlin协程系列的文章。
故乡的樱花开了
2023/11/24
2630
推荐阅读
相关推荐
抽丝剥茧Kotlin - 协程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档