kotlin协程硬核解读(6.协程调度器实现原理)(代码片段)

open-Xu open-Xu     2023-03-09     200

关键词:

版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载

《kotlin协程硬核解读(4. 协程的创建和启动流程分析)》一文中,我们分析了协程自创建器创建协程对象到开始执行协程代码块中代码的整个过程,文章最后总结了协程有3层包装:

  • 第一层是通过协程构建器创建的AbstractCoroutine子类类型的协程对象,它的作用是维护了协程的上下文
  • 第二层是编译期生成的SuspendLambda的子类对象,封装了协程代码块中的代码和执行逻辑
  • 第三层是DispatchedContinuation,封装了协程的线程调度,也就是决定协程代码块中的代码是在那个线程上执行的

在kotlin协程中,协程的执行涉及到3次线程切换,分别是:

  • 切换到指定线程执行协程代码块中的代码
  • 当协程代码块调用到异步挂起函数时,切换到指定线程执行挂起函数
  • 当异步挂起函数执行完毕,将函数执行结果Result对象切换到协程所在的线程,继续执行协程代码块中剩下的代码

这些线程切换的动作都是通过协程调度器来实现的,本文将对协程调度器做详细讲解。

1. 相关类介绍

1.1 ContinuationInterceptor续体拦截器

《kotlin协程硬核解读(3. suspend挂起函数&挂起和恢复的实现原理)》一文的2.3 SuspendLambda节我们讲过续体Continuation相关的类,其第二层实现类ContinuationImpl(第一层是BaseContinuationImpl)主要就是增加了续体拦截器的功能函数intercepted(),从协程上下文中获取KeyContinuationInterceptor的续体拦截器上下文对象。续体拦截器的作用就是将原始续体对象包装为另一种续体类型的对象,从而增强原续体的功能

//续体拦截器,它是一个协程上下文元素
public interface ContinuationInterceptor : CoroutineContext.Element 
    //续体拦截器的键
    companion object Key : CoroutineContext.Key<ContinuationInterceptor>
	//拦截续体,将原始续体(根据前面文章的分析,原始续体通常就是SuspendLambda的子类对象)转换为另一种续体子类类型
    public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
	...

1.2 Dispatchers调度器

kotlin协程库中有4种调度器,它们都是CoroutineDispatcher的子类对象,而CoroutineDispatcher又实现了ContinuationInterceptor,所以调度器是通过续体拦截器实现的,每个调度器对象是上下文元素同时又是一个续体拦截器:

//Dispatchers单例对象,它包含4中调度器对象
public actual object Dispatchers 
    //4种调度器,都是CoroutineDispatcher的子类对象,CoroutineDispatcher是通过续体拦截器实现的。actual表示与平台相关,不同平台实现不同
    public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
    public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
    public val IO: CoroutineDispatcher = DefaultScheduler.IO

//协程调度器抽象类,定义了切换线程的相关方法
public abstract class CoroutineDispatcher :
	AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor 
    public companion object Key : AbstractCoroutineContextKey<ContinuationInterceptor, CoroutineDispatcher>(
        ContinuationInterceptor,   it as? CoroutineDispatcher )
    
        //注意是final修饰的:所有的调度器(续体拦截器)都将原续体包装为DispatchedContinuation类型
	public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
		DispatchedContinuation(this, continuation)
        
    //四种调度器都分别实现下面两个函数,确保在不同的线程调度
	//判断是否需要切换线程,默认为true,表示需要切换线程,具体的调度器需要根绝情况重写
	public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
	//调度:切换线程执行block
	public abstract fun dispatch(context: CoroutineContext, block: Runnable)




//类关系图
CoroutineContext
	.Element        //上下文元素
		|-- ContinuationInterceptor //续体拦截器:将续体包装为另一种续体类型
				|-- CoroutineDispatcher //协程调度器抽象类:实现了拦截器功能,将原续体对象包装为DispatchedContinuation类型
						//四种调度器,实现了CoroutineDispatcher,不同的平台实现不同
						|-- Dispatchers.Default   //
						|-- Dispatchers.Main      //
						|-- Dispatchers.Unconfined//
						|-- Dispatchers.IO        //

2. 协程的3次线程调度

2.1 第一次线程调度:切换到指定线程开始执行协程代码

《kotlin协程硬核解读(4. 协程的创建和启动流程分析)》一文中的第2.1章节,当使用协程构建器创建一个协程对象时,会在newCoroutineContext(context)函数中检查作用域的上下文中是否存在key为ContinuationInterceptor的元素,如果不存在将会添加一个Dispatchers.Default对象,所以协程的上下文中必定会有一个ContinuationInterceptor的元素,也就是线程调度器,来明确协程代码在哪个线程上执行

//☆ kotlin-stlib.jar 
package kotlin.coroutines.jvm.internal
//续体的实现,增加了拦截功能
internal abstract class ContinuationImpl(
    completion: Continuation<Any?>?, private val _context: CoroutineContext?) : BaseContinuationImpl(completion) 
    public override val context: CoroutineContext
        get() = _context!!
    private var intercepted: Continuation<Any?>? = null   //被拦截的原始续体对象(SuspendLambda子类对象)
    //3.1 从上下文中获取拦截器,实现续体对象包装
    public fun intercepted(): Continuation<Any?> =
        intercepted?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also  intercepted = it 
       ...

在启动协程之前,会调用createCoroutineUnintercepted(receiver, completion)创建SuspendLambda子类类型的原始续体对象,然后调用续体的intercepted()将其包装为DispatchedContinuation类型,最后调用resumeCancellableWith()启动协程。resumeCancellableWith()函数中使用调度器对象dispatcher判断是否需要切换线程,如果需要则调用dispatcher.dispatch()实现线程切换

//☆ kotlin-coroutines-core-jvm.jar 
//☆ CoroutineContext.kt
package kotlinx.coroutines
public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext 
    val combined = coroutineContext + context
    val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
    //★1. 如果上下文中不存在ContinuationInterceptor类型的元素,添加默认的线程调度器Dispatchers.Default
    return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
        debug + Dispatchers.Default else debug


//☆ Cancellable.kt
package kotlinx.coroutines.intrinsics
//启动协程
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
    receiver: R, completion: Continuation<T>, onCancellation: ((cause: Throwable) -> Unit)? = null) =
    runSafely(completion) 
        createCoroutineUnintercepted(receiver, completion)    //★2. 重新创建续体对象(SuspendLambda子类对象)
        	.intercepted()    //★3. 从协程上下文中获取调度器拦截原续体,将其包装为DispatchedContinuation类型
        	.resumeCancellableWith(Result.success(Unit), onCancellation)  //★4. 启动协程
    

//☆ DispatchedContinuation.kt
package kotlinx.coroutines.internal
internal class DispatchedContinuation<in T>(   //调度器续体包装类
    @JvmField val dispatcher: CoroutineDispatcher,  //持有调度器对象
    @JvmField val continuation: Continuation<T>     //持有原始续体对象
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation 
    public fun <T> Continuation<T>.resumeCancellableWith(result: Result<T>, onCancellation: ((cause: Throwable) -> Unit)? = null): Unit = when (this) 
        //★5. 如果续体类型是DispatchedContinuation,调用resumeCancellableWith()启动协程
        is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
        else -> resumeWith(result)
    
    inline fun resumeCancellableWith(result: Result<T>, noinline onCancellation: ((cause: Throwable) -> Unit)?) 
        val state = result.toState(onCancellation)
        if (dispatcher.isDispatchNeeded(context))    //★6. 判断是否需要切换线程
            _state = state
            resumeMode = MODE_CANCELLABLE
            dispatcher.dispatch(context, this)    //★7. 切换线程后开始执行协程代码
         else 
            executeUnconfined(state, MODE_CANCELLABLE) 
                if (!resumeCancelled(state)) 
                    resumeUndispatchedWith(result)
                
            
        
    

2.2 第二次线程调度:切换线程执行异步挂起函数

《kotlin协程硬核解读(3. suspend挂起函数&挂起和恢复的实现原理)》一文中讲解了怎样自定义异步挂起函数,要想挂起函数相对于协程异步执行,有两种方式:

//方式1:调用suspendCancellableCoroutine()定义挂起函数,直接在函数中开启Thread,在子线程中执行函数体
suspend fun getUser(): User = suspendCancellableCoroutine 
    continuation ->
    Thread  //也可以通过线程池异步执行,retrofit对协程的支持网络请求就是通过okhttp的异步请求实现的(线程池)
        ...
        continuation.resume(User("openXu"))
    .start()


//方式2:调用withContext(...)定义挂起函数,通过传入调度器上下文实现线程切换
suspend fun getUser(): User = withContext(Dispatchers.IO) 
    ...
    User("openXu")   //子协程返回值

第一种方式没什么说的,不管是通过线程池还是Thread,都能将函数体切换到子线程执行。第二种方式withContext(Dispatchers.IO)当判断传入的调度器和父协程调度器不同时,会创建一个DispatchedCoroutine类型的子协程,函数体就是子协程的挂起代码块,当执行子协程时,又会走2.1的步骤切换到指定线程开始执行子协程代码

public suspend fun <T> withContext(
    context: CoroutineContext,       //协程上下文,一般情况下传递一个调度器
    block: suspend CoroutineScope.() -> T   //子协程代码块
): T 
    ...
    return suspendCoroutineUninterceptedOrReturn sc@  uCont ->
        val oldContext = uCont.context         //父协程的上下文
        val newContext = oldContext + context  //父协程上下文+新的调度器
        ...
        if (newContext === oldContext) 
            //新的上下文和父协程上下文地址相同,不需要切换线程,将创建一个ScopeCoroutine类型的子协程
            val coroutine = ScopeCoroutine(newContext, uCont)
            return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
        
        // 如果传入的调度器和父协程调度器相同,不需要切换线程,将创建一个UndispatchedCoroutine类型(ScopeCoroutine子类)的子协程
        if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) 
            val coroutine = UndispatchedCoroutine(newContext, uCont)
            withCoroutineContext(newContext, null) 
                return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
            
        
        //★上面两种不需要切换线程的就没必要跟踪了。当调度器不同时创建一个DispatchedCoroutine类型的子协程,切换到新线程执行子协程代码块(也就是函数体)
        val coroutine = DispatchedCoroutine(newContext, uCont)
        coroutine.initParentJob()
        //★★★ 接2.1的startCoroutineCancellable(),创建子SuspendLambda续体对象,然后包装为DispatchedContinuation类型,最后dispatcher.dispatch()切换到新线程开始执行函数体
        block.startCoroutineCancellable(coroutine, coroutine)   
        coroutine.getResult()
    

2.3第三次线程调度:异步挂起函数执行完毕后将函数执行结果切回协程所在线程并恢复协程执行

执行异步挂起函数时切换到新的协程执行函数有两种方式,当函数执行完毕切回到协程所在线程同样有两种方式。

对于第一种在函数体内通过线程池或者Thread的方式,当函数执行完毕会调用续体continuationresumeWith()方法恢复协程的执行,而continuation是协程被包装后的DispatchedContinuation类型,所以就是调用DispatchedContinuationresumeWith()

第二种通过withContext()创建了一个子协程,根据《kotlin协程硬核解读(3. suspend挂起函数&挂起和恢复的实现原理》一文中<2.5 挂起、恢复实现原理源码解读>,协程代码块的代码会通过状态机被分为多个执行部分放入的SuspendLambda.invokeSuspend()函数中,invokeSuspend()又是在子协程的续体BaseContinuationImpl.resumeWith()中调用的,所以子协程代码块的返回值将作为最后一次调用invokeSuspend()函数的返回值,在BaseContinuationImpl.resumeWith()函数中这个返回值将被传递给子协程的AbstractCoroutine.resumeWith()函数作为子协程的返回值。withContext()创建的子协程DispatchedCoroutine重写了afterResume()函数,该函数中调用父协程的续体的intercepted()函数再次将父协程续体包装为DispatchedContinuation类型,然后调用resumeCancellableWith()切换线程,将挂起函数的结果返回并恢复父协程执行。

//所有协程对象的抽象父类
public abstract class AbstractCoroutine<in T>(protected val parentContext: CoroutineContext, active: Boolean = true) 
	: JobSupport(active), Job, Continuation<T>, CoroutineScope 
    public final override fun resumeWith(result: Result<T>) 
        val state = makeCompletingOnce(result.toState())   //尝试设置当前协程状态为已完成
        if (state === COMPLETING_WAITING_CHILDREN) return
        afterResume(state)
    
    protected open fun afterResume(state: Any?): Unit = afterCompletion(state) //afterCompletion()是一个空实现,因为普通的协程代码块不需要返回值


//withContext()创建的调度器子协程类型
private class DispatchedCoroutine<in T>(
    context: CoroutineContext,   //父协程上下文+新的调度器
    uCont: Continuation<T>       //★ 持有父协程的续体对象,用于将子协程的执行结果切回到父协程的执行线程
) : ScopeCoroutine<T>(context, uCont) 
    ...
    //重写afterResume()函数,因为withContext()函数是有返回值的,子协程代码块的返回值将作为withContext()函数的返回值
    override fun afterResume(state: Any?) 
        // 通过父协程的续体,切换回父协程所在的线程恢复父协程执行
        uCont
        	.intercepted()  //★ 调用父协程的续体的intercepted(),这个函数将从父协程上下文中获取父协程的调度器,并将父协程续体再次包装为DispatchedContinuation类型
        	.resumeCancellableWith(recoverResult(state, uCont))  //恢复父协程执行,并将子协程的返回值传给父协程(也就是withContext()函数的返回值)
    

2.4 小结

通过上面的对协程执行过程中的3次线程切换的源码跟踪,发现协程中的线程切换都会调用DispatchedContinuationresumeWith()或者resumeCancellableWith()函数,通过调度器对象dispatcher.isDispatchNeeded()判断是否需要切换线程,如果需要则调用dispatcher.dispatch()实现线程切换,然后在切换后的线程上执行Runnable调用原续体的resumeWith()从而触发invokeSuspend()启动或恢复协程执行:

//调度器续体,协程的第3层包装(第一层是协程对象,第二层是SuspendLambda)
internal class DispatchedContinuation<in T>(
    @JvmField val dispatcher: CoroutineDispatcher,
    @JvmField val continuation: Continuation<T>  //原续体对象,可能是SuspendLambda,也可能是DispatchedContinuation
) : DispatchedTask<T>(MODE_UNINITIALIZED), CoroutineStackFrame, Continuation<T> by continuation 
	/**
	 * 重写了续体的resumeWith()函数,在原有的恢复协程执行的基础上增加了线程切换功能,该方法会从挂起函数执行线程 切回到 协程调度线程
	 * resumeWith():挂起函数执行完毕后,通过调用resume系列函数恢复函数执行结果或者异常最终都会调用到该方法,恢复协程执行
	 * resumeCancellableWith():通常是启动协程、子协程执行完毕返回结果恢复父协程执行时调用
	 * resumeCancellableWith()和resumeWith()实现差不多,就多了一个是否cancel的判断,这里就不粘贴代码了
	 */
    override fun resumeWith(result: Result<T>) 
        ...
        //★1. 判断是否需要切换线程
        if (dispatcher.isDispatchNeeded(context)) 
            _state = state
            resumeMode = MODE_ATOMIC
            //★2. 调度器切换线程,然后执行this,也就是在切换后的线程上执行下面的run()方法
            dispatcher.dispatch(context, this)
         else 
        	//不需要切换线程时,直接调用原续体的resumeWith()
            executeUnconfined(state, MODE_ATOMIC) 
                withCoroutineContext(this.context, countOrElement) 
                    continuation.resumeWith(result)
                
            
        
    
    
    //this就是代理续体
    override val delegate: Continuation<T>
        get() = this
    
    //DispatchedTask实现了Runnable,DispatchedContinuation继承了它,第2步切换线程时传入的this就表示在指定的线程执行run方法(已经切换线程后执行)
    public final override fun run() 
        ...
        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 (exception == null && resumeMode.isCancellableMode) context[Job] else null
            //★3. 在切换后的线程中调用原续体continuation的resumeWith()从而触发invokeSuspend()启动或恢复协程执行
            if (job != null && !job.isActive) 
                val cause = job.getCancellationException()
                cancelCompletedResult(state, cause)
                continuation.resumeWithStackTrace(cause)   //
             else 
                if (exception != null) 
                    continuation.resumeWithException(exception) //
                 else 
                    continuation.resume(getSuccessfulResult(state))//
                
            
        
        ...
    
    

3. 调度器实现线程切换的原理

3.1 调度器的平台实现

协程库中有一个Dispatchers单例对象,该对象包含协程的4中调度器成员变量,这些变量都是协程调度器CoroutineDispatcher及其子类的对象:

//Dispatchers单例对象,它包含4中调度器对象
public actual object Dispatchers 
    //4种调度器,都是CoroutineDispatcher的子类对象,CoroutineDispatcher是通过续体拦截器实现的。actual表示与平台相关,不同平台实现不同
    public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
    public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
    public val IO: CoroutineDispatcher = DefaultScheduler.IO

注意观察除了IO外,DefaultMainUnconfined都被actual修饰,actual是kotlin的关键字,表示平台相关的实现。我们知道kotlin是一个跨平台的语言,它可以被编译成各种平台的可执行指令,比如Jvm、Android、js、Native、iOS、Linux、Windows等,在不同的平台上实现有些API难免会有差异。比如一般情况下带有UI相关的平台才会强调主线程,主线程专门用于刷新UI不能进行耗时操作导致UI卡顿,而其他平台如普通jvm上不强调主线程(也就不需要主线程调度器),所以对于Main调度器的实现在不同平台上实现是不一样的。如果我们在普通jvm平台程序中使用Dispatchers.Main会抛异常,说缺少具有Main调度器的模块,需要添加提供Main调度器的依赖,比如kotlinx-coroutines-android扩展包

fun main()
    runBlocking 
        //普通jvm平台没有Main调度器,测试中可以添加kotlinx-coroutines-test.jar依赖
        withContext(Dispatchers.Main)  
    

运行结果:
Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, ekotlin协程硬核解读(6.协程调度器实现原理)(代码片段)

...xff08;java任务拆分式线程池调度器)DefaultScheduler(kotlin实现的任务抢占式线程池调度器)3.2.2Dispatchers.Main3.2.3Dispatchers.Unconfined3.2.4Dispatchers.IO3.3总结在《kotlin协程硬核解读(4.协程的创建和启动流程分析)》一文中,我... 查看详情

kotlin协程硬核解读(2.协程基础使用&源码浅析)(代码片段)

版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载文章目录1.依赖2.相关概念及术语3.协程构建器3.1runBlocking3.2launch3.3async3.4怎样获取CoroutineScope的实例对象?3.5其他构建器(构建子协程)3... 查看详情

kotlin协程硬核解读(2.协程基础使用&源码浅析)(代码片段)

版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载文章目录1.依赖2.相关概念及术语3.协程构建器3.1runBlocking3.2launch3.3async3.4怎样获取CoroutineScope的实例对象?3.5其他构建器(构建子协程)3... 查看详情

kotlin协程硬核解读(2.协程基础使用&源码浅析)(代码片段)

版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载文章目录1.依赖2.相关概念及术语3.协程构建器3.1runBlocking3.2launch3.3async3.4怎样获取CoroutineScope的实例对象?3.5其他构建器(构建子协程)3... 查看详情

kotlin协程硬核解读(1.协程初体验)(代码片段)

...步的方式编写异步代码2.5协程只是为了消灭回调?3.kotlin协程出现的原因3.1CallBack异步回调3.2Future阻塞获取异步计算结果3.3协程非阻 查看详情

kotlin协程硬核解读(4.协程的创建和启动流程分析)(代码片段)

...utineScope协程作用域1.2AbstractCoroutine协程1.3Continuation续体1.4kotlin扩展函数1.5kotlin的函数类型Function1.6调用操作符()重载2.协程的启动、执行流程分析2.1CoroutineScope.launch()创建协程对象2.2initParentJob()父子Job绑定,传递取消2.3CoroutineSt... 查看详情

kotlin协程硬核解读(4.协程的创建和启动流程分析)(代码片段)

...utineScope协程作用域1.2AbstractCoroutine协程1.3Continuation续体1.4kotlin扩展函数1.5kotlin的函数类型Function1.6调用操作符()重载2.协程的启动、执行流程分析2.1CoroutineScope.launch()创建协程对象2.2initParentJob()父子Job绑定,传递取消2.3CoroutineSt... 查看详情

kotlin协程硬核解读(3.suspend挂起函数&挂起和恢复的实现原理)(代码片段)

版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载文章目录1.自定义挂起函数1.1为什么需要自定义挂起函数1.2suspend到底有什么用?1.3不完全挂起函数(组合挂起函数)1.4真正... 查看详情

kotlin协程硬核解读(5.java异常本质&协程异常传播取消和异常处理机制)(代码片段)

版权声明:本文为openXu原创文章【openXu的博客】,未经博主允许不得以任何形式转载文章目录1.异常的本质1.1操作系统、程序、JVM、进程、线程1.2异常方法调用栈1.3java异常处理机制1.4小结2.Android异常处理机制3.协程异常3.1... 查看详情

kotlin协程硬核解读(5.java异常本质&协程异常传播取消和异常处理机制)(代码片段)

...串行中的异常处理4.3并发中的异常处理4.4总结本文承接《kotlin协程硬核解读(2.协程基础使用&源码浅析)》中的第8章节1.异常的本质平时在开发中当程序throw抛出了一个异常对象而没有被try-catch处理掉的话,会在控制台看到... 查看详情

kotlin协程硬核解读(1.协程初体验)(代码片段)

...步的方式编写异步代码2.5协程只是为了消灭回调?3.kotlin协程出现的原因3.1CallBack异步回调3.2Future阻塞获取异步计算结果3.3协程非阻塞挂起4.总结公司项目中使用协程已经有一段时间了,刚开始接触协程的时候也和其他做An... 查看详情

kotlin协程协程底层实现②(协程调度器|协程任务泄漏|结构化并发)

文章目录一、协程调度器二、协程任务泄漏三、结构化并发一、协程调度器协程是在调度器中运行的,在协程中有3种调度器:Dispatchers.Main调度器:在主线程中运行,处理UI交互任务;使用场景如:调用挂起suspend函数,更新UI,更新LiveData;Dis... 查看详情

kotlin协程协程底层实现②(协程调度器|协程任务泄漏|结构化并发)

文章目录一、协程调度器二、协程任务泄漏三、结构化并发一、协程调度器协程是在调度器中运行的,在协程中有3种调度器:Dispatchers.Main调度器:在主线程中运行,处理UI交互任务;使用场景如:调用挂起suspend函数,更新UI,更新LiveData;Dis... 查看详情

深入理解kotlin协程协程调度器dispatchers源码追踪扒皮(代码片段)

Android中的Kotlin协程调度器主要有四种:Dispatchers.Main、Dispatchers.Default、Dispatchers.IO、Dispatchers.Unconfined,第四种平时开发应该一般不会用到,所以主要了解一下前3种的实现。Dispatchers.MainpublicactualvalMain:MainCorouti 查看详情

深潜kotlin协程:dispatchers协程调度器(代码片段)

系列电子书:传送门Kotlin协程库提供的一个重要的功能是让我们能决定协程应该运行在哪个线程(或线程池)上,这是通过使用调度器来完成的。在英语词典中,dispatcher的意思是“负责将人员或者车辆运送到... 查看详情

深潜kotlin协程:dispatchers协程调度器(代码片段)

系列电子书:传送门Kotlin协程库提供的一个重要的功能是让我们能决定协程应该运行在哪个线程(或线程池)上,这是通过使用调度器来完成的。在英语词典中,dispatcher的意思是“负责将人员或者车辆运送到... 查看详情

kotlin协程协程启动⑤(协程作用域构建器|runblocking函数|coroutinescope函数|supervisorscope函数)(代码片段)

...、supervisorScope协程作用域构建器示例一、结构化并发在【Kotlin协程】协程底层实现②(协程调度器|协程任务 查看详情

kotlin协程协程启动⑤(协程作用域构建器|runblocking函数|coroutinescope函数|supervisorscope函数)(代码片段)

...、supervisorScope协程作用域构建器示例一、结构化并发在【Kotlin协程】协程底层实现②(协程调度器|协程任务 查看详情