Kotlin協程原理解析 1. Kotlin協程作用 2. cps轉換 3. 協程狀態機 5. 協程啓動 6.協程掛起 7.協程恢復

1. Kotlin協程作用

Kotlin協程是一套基於Java Thread的線程框架,最大的特點就是可以1,用同步的方式寫出異步代碼,並且2,不阻塞當前線程。

2. cps轉換

2.1 cps轉換示例

//編譯前
private suspend fun testCPS(): String {
     withContext(Dispatchers.IO) {
        return "testCPS"
       }
  }
 
//編譯後
private final Object testCPS(Continuation $completion) {
      return "testCPS";
   }

Kotlin 的編譯器檢測到 suspend 關鍵字修飾的函數後,會進行cps轉換,轉換點:

1,函數中增加了一個Continuation類型的參數;

2,函數返回值變爲Object(本例String->Object)

注意:這裏suspend函數即使沒有使用withContext開啓一個協程,也會進行cps轉換。說明只要suspend函數,不管開不開啓協程,編譯器都會對其進行cps轉換。

2.2 參數Continuation

public interface Continuation<in T> {
    public val context: CoroutineContext
//      相當於 onSuccess     結果   
//                 ↓         ↓
    public fun resumeWith(result: Result<T>)
}

interface CallBack {
    void onSuccess(String response);
}

1,Continuation :續體,可以理解爲剩餘要執行的代碼。協程體中的異步操作被狀態機分割成不同的片段,分片段執行,執行完一部分,剩下的部分叫做續體。

2,Continuation 是一個接口,和一般回調接口定義類似,可以判斷,協程的思想其實就是回調。

Continuation 定義看一個協程上下文屬性context,一個方法聲明resumeWith(),用於協程1,啓動(DispatchedContinuation),2,掛起時恢復(BaseContinuationImpl),或者3,協程運行完成時的回調(AbstractCoroutine);

2.2 返回值 Object

爲什麼返回值從String->Object?


public suspend fun <T> withContext(
    context: CoroutineContext,
    block: suspend CoroutineScope.() -> T
): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
        ...
        val coroutine = DispatchedCoroutine(newContext, uCont)
        coroutine.initParentJob()
        block.startCoroutineCancellable(coroutine, coroutine)
        coroutine.getResult()
    }
}

fun getResult(): Any? {
        if (trySuspend()) return COROUTINE_SUSPENDED
        // otherwise, onCompletionInternal was already invoked & invoked tryResume, and the result is in the state
        val state = this.state.unboxState()
        if (state is CompletedExceptionally) throw state.cause
        @Suppress("UNCHECKED_CAST")
        return state as T
    }

可以看出withContext的返回值有兩種:

1,如果當前withContext中的異步操作沒有完成,返回COROUTINE_SUSPENDED,協程框架根據這個字段掛起協程(其實就是直接return);

2,如果當前withContext中的異步操作已經完成,返回對應操作執行後的返回值(對應String)

3. 協程狀態機

3.1 協程狀態機


//編譯前
class TestCoroutine {
    private fun startCoroutine() {
        // funTest協程體
        val funTest: suspend CoroutineScope.() -> Unit = {
            println("funTest")
            suspendFun1()
            suspendFun2()
        }
        GlobalScope.launch(Dispatchers.Default, block = funTest)
    }

    // 掛起函數
    suspend fun suspendFun1() {
        println("suspendFun1")
    }
    // 掛起函數
    suspend fun suspendFun2() {
        println("suspendFun2")
    }
}

//編譯後
public final class TestCoroutine {
   private final void startCoroutine() {
      Function2 funTest = (Function2)(new Function2((Continuation)null) {
         int label;

         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            TestCoroutine var10000;
            switch(this.label) {
            case 0:
               ResultKt.throwOnFailure($result);
               String var2 = "funTest";
               boolean var3 = false;
               System.out.println(var2);
               var10000 = TestCoroutine.this;
               this.label = 1;
               if (var10000.suspendFun1(this) == var4) {
                  return var4;
               }
               break;
            case 1:
               ResultKt.throwOnFailure($result);
               break;
            case 2:
               ResultKt.throwOnFailure($result);
               return Unit.INSTANCE;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }

            var10000 = TestCoroutine.this;
            this.label = 2;
            if (var10000.suspendFun2(this) == var4) {
               return var4;
            } else {
               return Unit.INSTANCE;
            }
         }

         @NotNull
         public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
            Intrinsics.checkNotNullParameter(completion, "completion");
            Function2 var3 = new <anonymous constructor>(completion);
            return var3;
         }

         public final Object invoke(Object var1, Object var2) {
            return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
         }
      });
      BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)Dispatchers.getDefault(), (CoroutineStart)null, funTest, 2, (Object)null);
   }

   @Nullable
   public final Object suspendFun1(@NotNull Continuation $completion) {
      System.out.println(""suspendFun1"");
      return Unit.INSTANCE;
   }

   @Nullable
   public final Object suspendFun2(@NotNull Continuation $completion) {
      System.out.println("suspendFun2");
      return Unit.INSTANCE;
   }
}

(其他文章描述)在反編譯的代碼中,協程體funTest被編譯成一個繼承SuspendLambda的類,在類中實現create(),invokeSuspend()兩個方法:

create()創建了一個協程體funTest類的實例;

invokeSuspend()方法執行具體的協程操作。

(我反編譯的代碼)沒搞懂的地方:

1,只創建了一個Function2的對象;

2,create方法創建的是匿名對象(anonymous constructor);

3,invoke方法是什麼作用

基本原理:

協程體會被編譯成一個SuspendLambda的子類,在這個類的invokeSuspend方法中,協程體中的suspend方法會被分割到switch不同的分支中,每個suspend方法會被cps機制轉換成帶有一個Continuation參數的方法。

通過一個label標籤控制分支代碼執行,label爲0,首先會進入第一個分支,首先將label設置爲下一個分支的數值,然後執行第一個suspend方法並傳遞當前Continuation,得到返回值,如果是COROUTINE_SUSPENDED,協程框架就直接return,協程掛起,當第一個suspend方法執行完成,會回調Continuation的invokeSuspend方法,進入第二個分支執行,以此類推執行完所有suspend方法。

如果suspend方法直接返回執行結果,那invokeSuspend後面的代碼怎麼執行?

3.2 SuspendLambda類圖

4 協程相關概念

4.1 CoroutineContext

launch函數是CoroutineScope的一個擴展函數,CoroutineScope只是一個接口,但是可以通過CoroutineScope的擴展方法進行協程的創建,除了launch函數還有async函數。

CoroutineScope除了通過擴展函數創建協程還有其它兩個作用,launch函數返回一個Job對象,可以通過這個Job管理協程,另外CoroutineScope爲協程提供一個上下文CoroutineContext。

CoroutineContext協程的上下文,這是一個數據集合接口聲明,協程中Job、Dispatcher調度器都可以是它的元素,CoroutineContext有一個非常好的作用就是我們可以通過它拿到Job、Dispatcher調度器等數據。

CombinedContext是CoroutineContext接口的具體實現類,存在兩個屬性,其中element是一個Element,代表集合的元素,left是一個CoroutineContext,代表鏈表的下一個節點。

通過CoroutineContext#plus可以看出,CoroutineContext的數據存儲方式是一個左向鏈表,鏈表的每一個節點是CombinedContext,並且存在攔截器的情況下,攔截器永遠是鏈表尾部的元素,這樣設計目的是因爲攔截器的使用頻率很高,爲了更快的讀取攔截器;

沒看懂這個左向鏈表實現,現在只要知道這是個集合,類似list,但是它有一個left元素始終在表尾,存儲攔截器,

4.2 CoroutineStart 啓動模式

CoroutineStart 是協程的啓動模式,存在以下4種模式:

DEFAULT 立即調度,可以在執行前被取消
LAZY 需要時才啓動,需要start、join等函數觸發纔可進行調度
ATOMIC 立即調度,協程肯定會執行,執行前不可以被取消
UNDISPATCHED 立即在當前線程執行,直到遇到第一個掛起點(可能切線程)

5. 協程啓動


// launch是CoroutineScope的一個擴展函數  
 public fun CoroutineScope.launch(  
     context: CoroutineContext = EmptyCoroutineContext,  
     start: CoroutineStart = CoroutineStart.DEFAULT,  
     block: suspend CoroutineScope.()
 ): Job {  
     // CoroutineContext創建一個新的Context
     val newContext = newCoroutineContext(context) 
     // 啓動模式的判斷
     val coroutine = if (start.isLazy)  
         LazyStandaloneCoroutine(newContext, block) else  
         StandaloneCoroutine(newContext, active = true)  
     coroutine.start(start, coroutine, block)  
     return coroutine  
 }

launch函數存在3個參數:

CoroutineContext 協程的上下文

CoroutineStart 協程的啓動模式

suspend CoroutineScope.() -> Unit 協程體

newCoroutineContext()是CoroutineScope的一個擴展方法,它的作用就是將傳參context與CoroutineScope中的CoroutineContext集合合併,並返回一個新的CoroutineContext,如果傳入Dispatchers.Default,就是將Dispatchers.Default與CoroutineScope中的CoroutineContext合併。

根據啓動模式,構建一個AbstractCoroutine的子類(協程對象都繼承AbstractCoroutine),如果是默認模式,則創建StandaloneCoroutine,並調用它的start方法。並將StandaloneCoroutine又作爲job返回。

5.1 AbstractCoroutine

AbstractCoroutine繼承或者實現了JobSupport、Job、Continuation、CoroutineScope。

JobSupport是Job的具體實現,AbstractCoroutine可以作爲一個Job控制協程的生命週期,同時實現Continuation接口,也可以作爲一個Continuation,重寫的resmueWith()方法的一個重要作用是恢復協程

AbstractCoroutine#resmueWith
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)  
    
  // JobSupport#afterCompletion  
  protected open fun afterCompletion(state: Any?) {}  

在AbstractCoroutine#resmueWith中首先根據JobSupport#makeCompletingOnce返回狀態判斷,協程是否處於等待子協程完成的狀態:

state == COMPLETING_WAITING_CHILDREN 等待子協程完成,自身才可完成。子協程完成後,觸發afterCompletion()

state != COMPLETING_WAITING_CHILDREN 沒有子協程或者所有子協程已經完成,自身可以完成,直接觸發afterCompletion()

協程對象可以通過重寫afterCompletion()處理協程完成之後的操作,下文中的協程恢復章節中,withContext()中DispatchedCoroutine協程對象,通過afterCompletion()恢復了外層的協程的運行。

AbstractCoroutine#start()
public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {  
       ...  
       //block :協程體  //receiver:協程對象  //this:AbstractCoroutine(也是協程對象)
       start(block, receiver, this)  
   }  
     
CoroutineStart#invoke
public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =  
    when (this) {  
        //completion:start傳過來的AbstractCoroutine  
        DEFAULT -> block.startCoroutineCancellable(receiver, completion)  
        ATOMIC -> block.startCoroutine(receiver, completion)  
        UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion) 
        LAZY -> Unit // will start lazily  
    }  
    
    
internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
    receiver: R, completion: Continuation<T>,
    onCancellation: ((cause: Throwable) -> Unit)? = null
) =
    runSafely(completion) {
        createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)
    }

AbstractCoroutine#start()調用了CoroutineStart的invoke()方法,然後根據啓動模式調用block對應的協程啓動方法,block.startCoroutineCancellable(receiver, completion) 中是一個鏈式調用流程。

createCoroutineUnintercepted() 
 public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(  
     completion: Continuation<T>  
 ): Continuation<Unit> {  
     // probeCompletion :AbstractCoroutine  
     val probeCompletion = probeCoroutineCreated(completion)  
     return if (this is BaseContinuationImpl)  
         create(probeCompletion)  
     else  
         createCoroutineFromSuspendFunction(probeCompletion) {  
             (this as Function1<Continuation<T>, Any?>).invoke(it)  
         }  
 }  
 
 
// continuation:AbstractCoroutine  
public final Continuation<Unit> create(Object obj, Continuation<?> continuation) {  
         ...  
         MainActivity$startCoroutine$funTest$1 mainActivity$startCoroutine$funTest$1 = new MainActivity$startCoroutine$funTest$1(this.this$0, continuation);  
         return mainActivity$startCoroutine$funTest$1;  
     }  

createCoroutineUnintercepted中通過調用create(probeCompletion)創建了一個協程體類的對象。

createCoroutineUnintercepted()是一個擴展函數,通過協程體block調用,所以源碼中this is BaseContinuationImpl的判斷中this指協程體類,編譯章節中協程體被編譯成SuspendLambda的子類;這裏的create函數就是SuspendLambda的子類中的create函數。

這裏的block對象和協程體類對象是什麼關係?有什麼區別?

注意看下構造函數的參數continuation,這裏continuation就是AbstractCoroutine,在協程體類的繼承鏈中,這個continuation一直傳遞到了BaseContinuationImpl父類中,用於後續協程的恢復。

注意:這裏將AbstractCoroutine對象傳遞給了協程類對象,進行了第一層代理。

繼續分析intercepted()

public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =  
     (this as? ContinuationImpl)?.intercepted() ?: this  
     
ContinuationImpl#intercepted
public fun intercepted(): Continuation<Any?> =  
        intercepted  
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this) .also { intercepted = it } 
       
CoroutineDispatcher#interceptContinuation
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
        //this是Dispatcher,continuation是協程體類對象
        DispatchedContinuation(this, continuation)

intercepted()協程體類對象轉換成ContinuationImpl,然後調用了ContinuationImpl的intercepted方法,intercepted方法中調用context[ContinuationInterceptor] 從協程類對象的CoroutineContext集合中取到調度器CoroutineDispatcher(這個CoroutineContext是launch是構建的,並傳遞到StandaloneCoroutine對象中),並調用調度器CoroutineDispatcher的interceptContinuation(),interceptContinuation()的作用是將協程體Continuation對象包裝成一個DispatchedContinuation。

注意:這裏將協程類對象傳遞給了DispatchedContinuationd對象,進行了第二層代理

5.2 CoroutineDispatcher

CoroutineDispatcher 
public abstract class CoroutineDispatcher :  
     AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {  
     ...  
     // 是否需要線程調度  
     public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true  
    // 線程調度,讓一個runable對象在指定線程運行  
    public abstract fun dispatch(context: CoroutineContext, block: Runnable)  
            // 將協程體對象continuation封裝爲一個DispatchedContinuation對象  
     public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =  
         DispatchedContinuation(this, continuation)     
            ...  
 }  

CoroutineDispatcher的作用是進行任務的線程切換。

CoroutineDispatcher實現了ContinuationInterceptor,代表是一個攔截器;

實現CoroutineContext接口,存儲在CoroutineContext的left節點。

CoroutineContext[ContinuationInterceptor]就可以在CoroutineContext集合中獲取到攔截器。

爲啥要通過攔截器去代理Continuation,直接使用DispatchedContinuation包裝不就行了嗎??

5.3 DispatchedContinuation

接着分析resumeCancellableWith(Result.success(Unit), onCancellation)

DispatchedContinuation 
internal class DispatchedContinuation<in T>(  
     // 調度器  
     @JvmField val dispatcher: CoroutineDispatcher,  
     // 協程體Continuation對象  
     @JvmField val continuation: Continuation<T>  
 ) : DispatchedTask<T>(MODE_ATOMIC_DEFAULT), CoroutineStackFrame, Continuation<T> by continuation {  
      
     // 使用delegate存儲當前對象  
     override val delegate: Continuation<T>  
         get() = this  
         
     // ATOMIC啓動模式  
     override fun resumeWith(result: Result<T>) {  
         val context = continuation.context  
         val state = result.toState()  
         // 是否需要線程調度  
         if (dispatcher.isDispatchNeeded(context)) {  
             _state = state  
             resumeMode = MODE_ATOMIC_DEFAULT  
             // dispatch 調度線程,第二個參數是一個Runnable類型,這裏傳參this也就是DispatchedContinuation自身  
             // DispatchedContinuation實際上也是一個Runnable對象,調用調度器的dispatch方法之後就可以使這個runnable在指定的線程運行了  
             dispatcher.dispatch(context, this)  
         } else {  
             executeUnconfined(state, MODE_ATOMIC_DEFAULT) {  
                 withCoroutineContext(this.context, countOrElement) {  
                     // 不需要調度,執行協程體的resumeWith  
                     continuation.resumeWith(result)  
                 }  
             }  
         }  
     }  
      // 默認啓動模式  
      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)  
                 }  
             }  
         }  
     }  
 }  

DispatchedContinuation 代理協程體類對象(SuspendLambda)並持有線程調度器(CoroutineDispatcher),它的作用就是使用線程調度器將協程體調度到指定的線程執行。

DispatchedContinuation也實現了Continuation接口,並重寫resumeWith(),首先
根據dispatcher.isDispatchNeeded(context)判斷需要線程切換:

1.如果需要線程調度,則調用dispatcher#dispatch進行調度,而dispatch()的第二個參數是一個runnable對象(這裏傳參爲this,即DispatchedContinuation對象本身,DispatchedContinuation同時還實現了Runnable接口),這個runnable就會運行在調度的線程上;

2.不需要調度則直接調用協程體類continuation對象的resumeWith(),前面的章節中提到,協程體的運行就是協程體類Continuation對象的resumeWith()被觸發,所以這裏就會讓協程體在當前線程運行;

另外還有一個方法resumeCancellableWith(),它和resumeWith()的實現很類似,在不同的啓動模式下調度線程的方法調用不同。比如默認的啓動模式調用resumeCancellableWith(),ATOMIC啓動模式則調用resumeWith()。

public actual object Dispatchers {
   
    @JvmStatic
    public actual val Default: CoroutineDispatcher = createDefaultDispatcher()

    @JvmStatic
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
    
    @JvmStatic
    public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
  
    @JvmStatic
    public val IO: CoroutineDispatcher = DefaultScheduler.IO
}

internal actual fun createDefaultDispatcher(): CoroutineDispatcher =
    if (useCoroutinesScheduler) DefaultScheduler else CommonPool

可以看到Default和IO 底層還是使用了線程池進行調度;Main使用了handler進行調度。

5.4 DispatchedTask

internal abstract class DispatchedTask<in T>(  
     @JvmField public var resumeMode: Int  
 ) : SchedulerTask() {  
 ’  
     // 在DispatchedContinuation中重寫了該屬性,delegate實際是指DispatchedContinuation對象  
     internal abstract val delegate: Continuation<T>  
   
     public final override fun run() {  
             ...  
             val delegate = delegate as DispatchedContinuation<T>  
             // 通過delegate拿到原始協程體Continuation對象  
             val continuation = delegate.continuation  
             ...  
             // 調用協程體類對象的resume  
             continuation.resume(getSuccessfulResult(state))  
             ...  
     }  
 }     

 // Continuation的擴展方法,觸發Continuation內的方法resumeWith  
 public inline fun <T> Continuation<T>.resume(value: T): Unit =  
     resumeWith(Result.success(value)) 

DispatchedTask的run方法中調用了協程體類對象的resume方法,間接調用了BaseContinuationImpl的resumeWith

注意:這裏是協程體類的resumeWith被執行了,不是DispatchedContinuation

5.5 BaseContinuationImpl

internal abstract class BaseContinuationImpl(  
     // completion:實參是一個AbstractCoroutine  
     public val completion: Continuation<Any?>?  
 ) : Continuation<Any?>, CoroutineStackFrame, Serializable {  
     public final override fun resumeWith(result: Result<Any?>) {  
         var current = this  
         var param = result  
         while (true) {  
             probeCoroutineResumed(current)  
             with(current) {  
                 val completion = completion!!   
                 val outcome: Result<Any?> =  
                     try {  
                         // 調用invokeSuspend方法,協程體真正開始執行  
                         val outcome = invokeSuspend(param)  
                         // invokeSuspend方法返回值爲COROUTINE_SUSPENDED,resumeWith方法被return,結束執行,說明執行了掛起操作  
                         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) {               
                     current = completion  
                     param = outcome  
                 } else {  
                     // 在示例代碼中,completion是一個AbstractCoroutine,是指launch函數創建的StandaloneCoroutine  
                     completion.resumeWith(outcome)  
                     return  
                 }  
             }  
         }  
     } 
     
    protected abstract fun invokeSuspend(result: Result<Any?>): Any?  
    
 }

BaseContinuationImpl定義了一個抽象方法invokeSuspend(),並重寫了Continuation的resumeWith(),並在其中調用invokeSuspend(),具體實現就是SuspendLambda的invokeSuspend(),invokeSuspend()方法中便是具體協程任務。

invokeSuspend()和invoke啥關係??

5.6 協程啓動流程總結:

以調度器爲Dispatchers.Default,啓動模式爲CoroutineStart.DEFAULT爲例:

  1. CoroutineScope#launch()創建一個協程,在其內部實現中根據啓動模式爲CoroutineStart.DEFAULT,創建一個StandaloneCoroutine協程對象,並觸發StandaloneCoroutine#start(start, coroutine, block);

  2. StandaloneCoroutine的父類是AbstractCoroutine,StandaloneCoroutine#start()的實現在其父類中,即AbstractCoroutine#start();

  3. 在AbstractCoroutine#start()中,觸發CoroutineStart#invoke();

  4. CoroutineStart#invoke()的處理邏輯中,根據調度器爲Dispatchers.Default,調用協程體的startCoroutineCancellable()方法;

  5. startCoroutineCancellable()的內部處理是一個鏈式調用:

createCoroutineUnintercepted(..).intercepted().resumeCancellableWith(Result.success(Unit))

createCoroutineUnintercepted()創建一個協程體類對象;
intercepted()使用攔截器(調度器)將協程體類對象包裝成DispatchedContinuation(DispatchedContinuation代理了協程體類Continuation對象,並持有調度器);
調用DispatchedContinuation#resumeCancellableWith()。

  1. 在DispatchedContinuation#resumeCancellableWith()中,使用線程調度器觸發dispatcher#dispatch(context, this)進行調度,該調度器爲Dispatchers.Default;

  2. Dispatchers.Default#dispatch()調度處理中,將DispatchedContinuation分發到CoroutineScheduler線程池中,由CoroutineScheduler分配一個線程Worker,最終在Woreder的run()方法中觸發了DispatchedContinuation的run(),其內部實現是使協程體Continuation對象的resumeWithI()得以執行,前文中分析到協程體的執行其實就是resumeWith()方法被調用,這樣協程體就可以在執行的線程中執行了。

協程啓動流程圖:

5.7 三個協程對象總結:

1,第一層協程對象AbstractCoroutine,主要處理協程狀態和恢復掛起協程。

2,第二層對象BaseContinuationImpl,由編譯器將我們的代碼轉換而成,利用狀態機實現代碼分段執行和協程掛起,並代理第一層對象。在其resumeWith方法中通過調用invokeSuspend()執行我們的任務代碼。

注意:resumeWith方法中調用completion.resumeWith(outcome)是恢復協程,這個completion是AbstractCoroutine(launch對應StandaloneCoroutine或者witchContext對應DispatchedCoroutine),不是BaseContinuationImpl的子類。

3,第三層對象DispatchedContinuation,對第二層協程對象進行代理,負責使用dispatcher進行調度任務。

注意:協程體類是持有了一個AbstractCoroutine,不是繼承

6.協程掛起

withContext()

 public suspend fun <T> withContext(  
     context: CoroutineContext,  
     block: suspend CoroutineScope.() -> T  
 ): T {  
     contract {  
         callsInPlace(block, InvocationKind.EXACTLY_ONCE)  
     }  
     // 返回啓動withContext的協程體  
     return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->  
         // 構建一個新的newContext,合併當前協程體以及withContext協程體的CoroutineContext  
         val oldContext = uCont.context  
         val newContext = oldContext + context  
         // 檢查協程是否活躍,如果線程處於非活躍的狀態,拋出cancle異常  
         newContext.checkCompletion()  
         ...  
         // DispatchedCoroutine也是一個AbstractCoroutine對象,負責協程完成的回調,  
         // 注意這裏的Continuation的傳參爲uCont,及發起withContext的協程對象  
         val coroutine = DispatchedCoroutine(newContext, uCont)  
         coroutine.initParentJob()  
                  // 和協程啓動的流程一樣,啓動withContext的協程  
         // 注意這裏的傳參coroutine爲DispatchedCoroutine,它持有需要恢復的協程  
         block.startCoroutineCancellable(coroutine, coroutine)  
         // 返回結果爲掛起還是完成  
         coroutine.getResult()  
     }  
 }  

在withContext()的源碼可以看到,withContext()的協程體的啓動和原有協程的啓動流程是一樣的,DispatchedCoroutin是AbstractCoroutine的一個子類,並且在創建DispatchedCoroutin時的傳參是外層協程體對象,這是因爲當withContext()的協程體完成的時候需要通過外層協程體對象恢復當前協程的運行。

先看下協程的掛起coroutine.getResult()的實現。

 // DispatchedCoroutine#getResult  
  fun getResult(): Any? {  
        // 返回COROUTINE_SUSPENDED,掛起  
        if (trySuspend()) return COROUTINE_SUSPENDED  
        val state = this.state.unboxState()  
        // 出現異常  
        if (state is CompletedExceptionally) throw state.cause  
        @Suppress("UNCHECKED_CAST")  
        // 未出現異常結果返回  
        return state as T  
    }  
    
  // DispatchedCoroutine#trySuspend  
  private val _decision = atomic(UNDECIDED)  
  private fun trySuspend(): Boolean {  
        _decision.loop { decision ->  
            when (decision) {  
                // compareAndSet原子操作,當前值與預期值一致時返回true,以原子方式更新自身的值  
                UNDECIDED -> if (this._decision.compareAndSet(UNDECIDED, SUSPENDED)) return true  
                RESUMED -> return false  
                else -> error("Already suspended")  
            }  
        }  
    } 

是否掛起,結束協程運行,關鍵在是否返回COROUTINE_SUSPENDED標誌,在getResult()方法中的處理邏輯,就是看trySuspend()是否返回true。

trySuspend()方法中,_decision默認爲UNDECIDED,預期的參數值傳參也爲UNDECIDED,所以,trySuspend返回true,最終getResult方法返回了COROUTINE_SUSPENDED,協程被掛起了。

7.協程恢復

withContext()啓動一個協程和launch類似,當執行到BaseContinuationImpl的resumeWith方法,調用invokeSuspend得到結果之後,會調用內部代理的completion.resumeWith(outcome)方法,這個completion是DispatchedCoroutine。

DispatchedCoroutine是AbstractCoroutine的子類,當協程完成時會調用它的內部方法resumeWith(),內部的處理邏輯最後會觸發JubSpuuort#afterCompletion(),而在DispatchedCoroutine中重寫了afterCompletion()。

private class DispatchedCoroutine<in T>(  
     context: CoroutineContext,  
     // 外部需要恢復的協程  
     uCont: Continuation<T>  
 ) : ScopeCoroutine<T>(context, uCont) {  
   
     override fun afterCompletion(state: Any?) {  
         afterResume(state)  
     }  
   
     override fun afterResume(state: Any?) {  
         // 在getResult()之前,協程已運行結束,未發生掛起,不需要恢復外層協程  
         if (tryResume()) return   
         // 獲取外部協程的DispatchedContinuation,去恢復外層協程  
         uCont.intercepted().resumeCancellableWith(recoverResult(state, uCont))  
     }  
 }

uCont.intercepted()獲取到外層協程的DispatchedContinuation,然後調用resumeCancellableWith方法,使用外層協程的dispatcher將任務的執行切換到之前的線程中去執行。再次調用到外層協程的BaseContinuationImpl#resumeWith方法,再次調用到外層協程類的invokeSuspend方法中,去執行剩餘代碼。如果能直接結果就調用外層協程的completion.resumeWith(outcome)結束協程(completion是外層協類對象中代理的AbstractCoroutine)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章