Android中的Coroutine协程原理详解

Android中的Coroutine协程原理详解

前言

协程是一个并发方案。也是一种思想。

传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高。但是面对计算密集型的任务不如多线程并行运算效率高。

不同的语言对于协程都有不同的实现,甚至同一种语言对于不同平台的操作系统都有对应的实现。

我们kotlin语言的协程是 coroutines for jvm的实现方式。底层原理也是利用java 线程。

基础知识

生态架构

Android中的Coroutine协程原理详解

相关依赖库

dependencies {
   // Kotlin
   implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32"

   // 协程核心库
   implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
   // 协程Android支持库
   implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"
   // 协程Java8支持库
   implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.4.3"

   // lifecycle对于协程的扩展封装
   implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
   implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
   implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
}
​

为什么一些人总觉得协程晦涩难懂?

1.网络上没有详细的关于协程的概念定义,每种语言、每个系统对其实现都不一样。可谓是众说纷纭,什么内核态用户态巴拉巴拉,很容易给我们带偏

2.kotlin的各种语法糖对我们造成的干扰。如:

  • 高阶函数
  • 源码实现类找不到

所以扎实的kotlin语法基本功是学习协程的前提。

实在看不懂得地方就反编译为java,以java最终翻译为准。

协程是什么?有什么用?

kotlin中的协程干的事就是把异步回调代码拍扁了,捋直了,让异步回调代码同步化。除此之外,没有任何特别之处。

创建一个协程,就是编译器背后偷偷生成一系列代码,比如说 状态机

通过 挂起恢复让状态机 状态流转实现把层层嵌套的回调代码变成像同步代码那样直观、简洁。

它不是什么线程框架,也不是什么高深的内核态,用户态。它其实对于咱们安卓来说,就是一个关于回调函数的语法糖。。。

本文将会围绕 挂起恢复彻底剖析协程的实现原理

Kotlin函数基础知识复习

再Kotlin中函数是一等公民,有自己的类型

函数类型

fun foo(){}
//类型为 () -> Unit
fun foo(p: Int){}
//类型为 (Int) -> String

class Foo{
    fun bar(p0: String,p1: Long):Any{}

}
//那么 bar 的类型为:Foo.(String,Long) -> Any
//Foo就是bar的 receiver。也可以写成 (Foo,String,Long) ->Any

函数引用

fun foo(){}
//引用是 ::foo
fun foo(p0: Int): String
//引用也是 ::foo

咋都一样?没办法,就这样规定的。使用的时候 只能靠编译器推断

val f: () -> Unit = ::foo //编译器会推断出是fun foo(){}
val g: (Int) -> String = ::foo //推断为fun foo(p0: Int): String

带Receiver的写法

class Foo{
    fun bar(p0: String,p1: Long):Any{}
}

val h: (Foo,String,Long) -> Any = Foo:bar

绑定receiver的函数引用:

val foo: Foo = Foo()
val m: (String,Long) -> Any = foo:bar

额外知识点

val x: (Foo,String,Long) -> Any = Foo:bar
val y: Function3<foo,string,long,any> = x

Foo.(String,Long) -> Any = (Foo,String,Long) ->Any = Function3<foo,string,long,any>

</foo,string,long,any></foo,string,long,any>

函数作为参数传递

fun yy(p: (Foo,String,Long)->Any){
    p(Foo(),"Hello",3L)//&#x76F4;&#x63A5;p()&#x5C31;&#x80FD;&#x8C03;&#x7528;
    //p.invoke(Foo(),"Hello",3L) &#x4E5F;&#x53EF;&#x4EE5;&#x7528;invoke&#x5F62;&#x5F0F;
}

Lambda

就是匿名函数,它跟普通函数比是没有名字的,听起来好像是废话

//&#x666E;&#x901A;&#x51FD;&#x6570;
fun func(){
   println("hello");
}
&#x200B;
//&#x53BB;&#x6389;&#x51FD;&#x6570;&#x540D; func,&#x5C31;&#x6210;&#x4E86;&#x533F;&#x540D;&#x51FD;&#x6570;
fun(){
   println("hello");
}
&#x200B;
//&#x53EF;&#x4EE5;&#x8D4B;&#x503C;&#x7ED9;&#x4E00;&#x4E2A;&#x53D8;&#x91CF;
val func = fun(){
   println("hello");
}
&#x200B;
//&#x533F;&#x540D;&#x51FD;&#x6570;&#x7684;&#x7C7B;&#x578B;
val func :()->Unit = fun(){
   println("hello");
}
&#x200B;
//Lambda&#x8868;&#x8FBE;&#x5F0F;
val func={
   print("Hello");
}
&#x200B;
//Lambda&#x7C7B;&#x578B;
val func :()->String = {
print("Hello");
"Hello" //&#x5982;&#x679C;&#x662F;Lambda&#x4E2D;&#xFF0C;&#x6700;&#x540E;&#x4E00;&#x884C;&#x88AB;&#x5F53;&#x4F5C;&#x8FD4;&#x56DE;&#x503C;&#xFF0C;&#x80FD;&#x7701;&#x6389;return&#x3002;&#x666E;&#x901A;&#x51FD;&#x6570;&#x5219;&#x4E0D;&#x884C;
}
&#x200B;
//&#x5E26;&#x53C2;&#x6570;Lambda
val f1: (Int)->Unit = {p:Int ->
print(p);
}
//&#x53EF;&#x8FDB;&#x4E00;&#x6B65;&#x7B80;&#x5316;&#x4E3A;
val f1 = {p:Int ->
print(p);
}
//&#x5F53;&#x53EA;&#x6709;&#x4E00;&#x4E2A;&#x53C2;&#x6570;&#x7684;&#x65F6;&#x5019;&#xFF0C;&#x8FD8;&#x53EF;&#x4EE5;&#x5199;&#x6210;
val f1: (Int)->Unit = {
   print(it);
}
&#x200B;

关于函数的个人经验总结

函数跟匿名函数看起来没啥区别,但是反编译为java后还是能看出点差异

如果只是用普通的函数,那么他跟普通java 函数没啥区别。

比如 fun a() 就是对应java方法 public void a(){}

但是如果通过函数引用(:: a)来用这个函数,那么他并不是直接调用 fun a()而是重新生成一个 Function0

挂起函数

suspend 修饰。

挂起函数中能调用任何函数。

非挂起函数只能调用非挂起函数。

换句话说,suspend函数只能在suspend函数中调用。

简单的挂起函数展示:

//com.example.studycoroutine.chapter.CoroutineRun.kt
suspend fun suspendFun(): Int {
    return 1;
}

挂起函数特殊在哪?

public static final Object suspendFun(Continuation completion) {
    return Boxing.boxInt(1);
}

这下理解suspend为啥只能在suspend里面调用了吧?

想要让道貌岸然的suspend函数干活必须要先满足它!!!就是给它里面塞入一颗球。

然后他想调用其他的suspend函数,只需将球继续塞到其它的suspend方法里面。

普通函数里没这玩意啊,所以压根没法调用suspend函数。。。

读到这里,想必各位会有一些疑问:

  • question1.这不是鸡生蛋生鸡的问题么?第一颗球是哪来的?
  • question2.为啥编译后返回值也变了?
  • question3.suspendFun 如果在协程体内被调用,那么他的球(completion)是谁?

标准库给我们提供的最原始工具

public fun <t> (suspend () -> T).startCoroutine(completion: Continuation<t>) {
   createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}
&#x200B;
public fun <t> (suspend () -> T).createCoroutine(completion: Continuation<t>): Continuation<unit> =
   SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)

</unit></t></t></t></t>

以一个最简单的方式启动一个协程。

Demo-K1

fun main() {
   val b = suspend {
       val a = hello2()
       a
  }
   b.createCoroutine(MyCompletionContinuation()).resume(Unit)
}
&#x200B;
suspend fun hello2() = suspendCoroutine<int> {
   thread{
       Thread.sleep(1000)
       it.resume(10086)
  }
}
&#x200B;
class MyContinuation() : Continuation<int> {
   override val context: CoroutineContext = CoroutineName("Co-01")
   override fun resumeWith(result: Result<int>) {
       log("MyContinuation resumeWith &#x7ED3;&#x679C; = ${result.getOrNull()}")
  }
}

</int></int></int>

两个创建协程函数区别

startCoroutine 没有返回值 ,而createCoroutine返回一个Continuation,不难看出是 SafeContinuation

好像看起来主要的区别就是startCoroutine直接调用resume(Unit),所以不用包装成SafeContinuation,而createCoroutine则返回一个SafeContinuation,因为不知道将会在何时何处调用resume,必须保证resume只调用一次,所以包装为safeContinuation

SafeContinuationd&#x7684;&#x4F5C;&#x7528;&#x662F;&#x4E3A;&#x4E86;&#x786E;&#x4FDD;&#x53EA;&#x6709;&#x53D1;&#x751F;&#x5F02;&#x6B65;&#x8C03;&#x7528;&#x65F6;&#x624D;&#x6302;&#x8D77;

分析createCoroutineUnintercepted

//kotlin.coroutines.intrinsics.CoroutinesIntrinsicsH.kt
@SinceKotlin("1.3")
public expect fun <t> (suspend () -> T).createCoroutineUnintercepted(completion: Continuation<t>): Continuation<unit>

</unit></t></t>

先说结论

其实可以简单的理解为kotlin层面的原语,就是返回一个协程体。

开始分析

引用代码Demo-K1首先b 是一个匿名函数,他肯定要被编译为一个 FunctionX,同时它还被 suspend修饰 所以它肯定跟普通匿名函数编译后不一样。

编译后的源码为

public static final void main() {
     Function1 var0 = (Function1)(new Function1((Continuation)null) {
        int label;
&#x200B;
        @Nullable
        public final Object invokeSuspend(@NotNull Object $result) {
           Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
           Object var10000;
           switch(this.label) {
           case 0:
              ResultKt.throwOnFailure($result);
              this.label = 1;
              var10000 = TestSampleKt.hello2(this);
              if (var10000 == var3) {
                 return var3;
              }
              break;
           case 1:
              ResultKt.throwOnFailure($result);
              var10000 = $result;
              break;
           default:
              throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
          }
&#x200B;
           int a = ((Number)var10000).intValue();
           return Boxing.boxInt(a);
        }
&#x200B;
        @NotNull
        public final Continuation create(@NotNull Continuation completion) {
           Intrinsics.checkParameterIsNotNull(completion, "completion");
           Function1 var2 = new <anonymous constructor>(completion);
           return var2;
        }
&#x200B;
        public final Object invoke(Object var1) {
           return((<undefinedtype>)this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);
        }
    });
     boolean var1 = false;
     Continuation var7 = ContinuationKt.createCoroutine(var0, (Continuation)(newMyContinuation()));
     Unit var8 = Unit.INSTANCE;
     boolean var2 = false;
     Companion var3 = Result.Companion;
     boolean var5 = false;
     Object var6 = Result.constructor-impl(var8);
     var7.resumeWith(var6);
  }

</undefinedtype></anonymous>

我们可以看到先是 Function1 var0 = new Function1创建了一个对象,此时跟协程没关系,这步只是编译器层面的匿名函数语法优化

如果直接

fun main() {
   suspend {
       val a = hello2()
       a
  }.createCoroutine(MyContinuation()).resume(Unit)
}

也是一样会创建 Function1 var0 = new Function1

解答question1

继续调用 createCoroutine

再继续 createCoroutineUnintercepted ,找到在JVM平台的实现

//kotlin.coroutines.intrinsics.IntrinsicsJVM.class
@SinceKotlin("1.3")
public actual fun <t> (suspend () -> T).createCoroutineUnintercepted(
   completion: Continuation<t>
): Continuation<unit> {
//probeCompletion&#x8FD8;&#x662F;&#x6211;&#x4EEC;&#x4F20;&#x5165;completion&#x5BF9;&#x8C61;&#xFF0C;&#x5728;&#x6211;&#x4EEC;&#x7684;Demo&#x5C31;&#x662F;myCoroutine
   val probeCompletion = probeCoroutineCreated(completion)//probeCoroutineCreated&#x65B9;&#x6CD5;&#x70B9;&#x8FDB;&#x53BB;&#x770B;&#x4E86;&#xFF0C;&#x597D;&#x50CF;&#x662F;debug&#x7528;&#x7684;.&#x6211;&#x7684;&#x7406;&#x89E3;&#x662F;&#x8FD9;&#x6837;&#x7684;
   //This&#x5C31;&#x662F;&#x8FD9;&#x4E2A;suspend lambda&#x3002;&#x5728;Demo&#x4E2D;&#x5C31;&#x662F;myCoroutineFun
   return if (this is BaseContinuationImpl)
       create(probeCompletion)
   else
//else&#x5206;&#x652F;&#x5728;&#x6211;&#x4EEC;demo&#x4E2D;&#x4E0D;&#x4F1A;&#x8D70;&#x5230;
     //&#x5F53; [createCoroutineUnintercepted] &#x9047;&#x5230;&#x4E0D;&#x7EE7;&#x627F; BaseContinuationImpl &#x7684;&#x6302;&#x8D77; lambda &#x65F6;&#xFF0C;&#x5C06;&#x4F7F;&#x7528;&#x6B64;&#x51FD;&#x6570;&#x3002;
       createCoroutineFromSuspendFunction(probeCompletion) {
          (this as Function1<continuation<t>, Any?>).invoke(it)
      }
}

</continuation<t></unit></t></t>
@NotNull
public final Continuation create(@NotNull Continuation completion) {
Intrinsics.checkNotNullParameter(completion, "completion");
Function1 var2 = new <anonymous constructor>(completion);
return var2;
}

</anonymous>

completion传入,并创建一个新的 Function1,作为 Continuation返回,这就是创建出来的协程体对象,协程的工作核心就是它内部的状态机, invokeSuspend函数

调用 create

@NotNull
public final Continuation create(@NotNull Continuation completion) {
    Intrinsics.checkNotNullParameter(completion, "completion");
    Function1 var2 = new <anonymous constructor>(completion);
    return var2;
}

</anonymous>

completion传入,并创建一个新的 Function1,作为 Continuation返回,这就是创建出来的协程体对象,协程的工作核心就是它内部的状态机, invokeSuspend函数

补充—相关类继承关系

Android中的Coroutine协程原理详解

解答question2&3

已知协程启动会调用协程体的resume,该调用最终会来到 BaseContinuationImpl::resumeWith

internal abstract class BaseContinuationImpl{
   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) {
           with(current) {
               val completion = completion!! // fail fast when trying to resume continuation without completion
               val outcome: Result<any?> =
                   try {
                       val outcome = invokeSuspend(param)//&#x8C03;&#x7528;&#x72B6;&#x6001;&#x673A;
                       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 {
                   //&#x6700;&#x7EC8;&#x8D70;&#x5230;&#x8FD9;&#x91CC;&#xFF0C;&#x8FD9;&#x4E2A;completion&#x5C31;&#x662F;&#x88AB;&#x585E;&#x7684;&#x7B2C;&#x4E00;&#x9897;&#x7403;&#x3002;
                   completion.resumeWith(outcome)
                   return
              }
          }
      }
  }
}

</any?></any?>

状态机代码截取

public final Object invokeSuspend(@NotNull Object $result) {
   Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
   Object var10000;
   switch(this.label) {
   case 0://&#x7B2C;&#x4E00;&#x6B21;&#x8FDB;&#x6765; label = 0
      ResultKt.throwOnFailure($result);
      // label&#x6539;&#x6210;1&#x4E86;&#xFF0C;&#x610F;&#x5473;&#x7740;&#x4E0B;&#x4E00;&#x6B21;&#x88AB;&#x6062;&#x590D;&#x7684;&#x65F6;&#x5019;&#x4F1A;&#x8D70;case 1&#xFF0C;&#x8FD9;&#x5C31;&#x662F;&#x6240;&#x8C13;&#x7684;&#x3010;&#x72B6;&#x6001;&#x6D41;&#x8F6C;&#x3011;
      this.label = 1;
      //&#x5168;&#x4F53;&#x76EE;&#x5149;&#x5411;&#x6211;&#x770B;&#x9F50;&#xFF0C;&#x6211;&#x5BA3;&#x5E03;&#x4E2A;&#x4E8B;&#xFF1A;this is &#x534F;&#x7A0B;&#x4F53;&#x5BF9;&#x8C61;&#x3002;
      var10000 = TestSampleKt.hello2(this);
      if (var10000 == var3) {
         return var3;
      }
      break;
   case 1:
      ResultKt.throwOnFailure($result);
      var10000 = $result;
      break;
   default:
      throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
   }

   int a = ((Number)var10000).intValue();
   return Boxing.boxInt(a);
}

question3答案出来了,传进去的是create创建的那个continuation

最后再来聊聊question2,从上面的代码已经很清楚的告诉我们为啥挂起函数反编译后的返回值变为object了。

以hello2为例子,hello2能返回代表挂起的白板,也能返回result。如果返回白板,状态机return,协程挂起。如果返回result,那么hello2执行完毕,是一个没有挂起的挂起函数,通常编译器也会提醒 suspend 修饰词无意义。所以这就是设计需要,没有啥因为所以。

最后,除了直接返回结果的情况,挂起函数一定会以resume结尾,要么返回result,要么返回异常。代表这个挂起函数返回了。

调用resume意义在于重新回调BaseContinuationImpl的resumeWith,进而唤醒状态机,继续执行协程体的代码。

换句话说,我们自定义的suspend函数,一定要利用suspendCoroutine 获得续体,即状态机对象,否则无法实现真正的挂起与resume。

suspendCoroutine

我们可以不用suspendCoroutine,用更直接的suspendCoroutineUninterceptedOrReturn也能实现,不过这种方式要手动返回白板。不过一定要小心,要在合理的情况下返回或者不返回,不然会产生很多意想不到的结果

suspend fun mySuspendOne() = suspendCoroutineUninterceptedOrReturn<string> { continuation ->
    thread {
        TimeUnit.SECONDS.sleep(1)
        continuation.resume("hello world")
    }
    //&#x56E0;&#x4E3A;&#x6211;&#x4EEC;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x6CA1;&#x6709;&#x8FD4;&#x56DE;&#x6B63;&#x786E;&#x7ED3;&#x679C;&#xFF0C;&#x6240;&#x4EE5;&#x5FC5;&#x987B;&#x8FD4;&#x56DE;&#x4E00;&#x4E2A;&#x6302;&#x8D77;&#x6807;&#x8BC6;,&#x5426;&#x5219;BaseContinuationImpl&#x4F1A;&#x8BA4;&#x4E3A;&#x5B8C;&#x6210;&#x4E86;&#x4EFB;&#x52A1;&#x3002;
    // &#x5E76;&#x4E14;&#x6211;&#x4EEC;&#x7684;&#x7EBF;&#x7A0B;&#x53C8;&#x5728;&#x8FD0;&#x884C;&#x6CA1;&#x6709;&#x53D6;&#x6D88;&#xFF0C;&#x8FD9;&#x5C06;&#x5F88;&#x591A;&#x610F;&#x60F3;&#x4E0D;&#x5230;&#x7684;&#x7ED3;&#x679C;
    kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}

</string>

而suspendCoroutine则没有这个隐患

suspend fun mySafeSuspendOne() = suspendCoroutine<string> { continuation ->
    thread {
        TimeUnit.SECONDS.sleep(1)
        continuation.resume("hello world")
    }
    //suspendCoroutine&#x51FD;&#x6570;&#x5F88;&#x806A;&#x660E;&#x7684;&#x5E2E;&#x6211;&#x4EEC;&#x5224;&#x65AD;&#x8FD4;&#x56DE;&#x7ED3;&#x679C;&#x5982;&#x679C;&#x4E0D;&#x662F;&#x60F3;&#x8981;&#x7684;&#x5BF9;&#x8C61;&#xFF0C;&#x81EA;&#x52A8;&#x8FD4;
    kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
}

</string>
public suspend inline fun <t> suspendCoroutine(crossinline block: (Continuation<t>) -> Unit): T =
    suspendCoroutineUninterceptedOrReturn { c: Continuation<t> ->
        //&#x5C01;&#x88C5;&#x4E00;&#x4E2A;&#x4EE3;&#x7406;Continuation&#x5BF9;&#x8C61;
        val safe = SafeContinuation(c.intercepted())
        block(safe)
        //&#x6839;&#x636E;block&#x8FD4;&#x56DE;&#x7ED3;&#x679C;&#x5224;&#x65AD;&#x8981;&#x4E0D;&#x8981;&#x8FD4;&#x56DE;COROUTINE_SUSPENDED
        safe.getOrThrow()
    }

</t></t></t>

SafeContinuation的奥秘

//&#x8C03;&#x7528;&#x5355;&#x53C2;&#x6570;&#x7684;&#x8FD9;&#x4E2A;&#x6784;&#x9020;&#x65B9;&#x6CD5;
internal actual constructor(delegate: Continuation<t>) : this(delegate, UNDECIDED)
@Volatile
private var result: Any? = initialResult //UNDECIDED&#x8D4B;&#x503C;&#x7ED9; result
//java&#x539F;&#x5B50;&#x5C5E;&#x6027;&#x66F4;&#x65B0;&#x5668;&#x90A3;&#x4E00;&#x5957;&#x4E1C;&#x897F;
private companion object {
       @Suppress("UNCHECKED_CAST")
       @JvmStatic
       private val RESULT = AtomicReferenceFieldUpdater.newUpdater<safecontinuation<*>, Any?>(
           SafeContinuation::class.java, Any::class.java as Class<any?>, "result"
      )
  }
&#x200B;
internal actual fun getOrThrow(): Any? {
   var result = this.result // atomic read
   if (result === UNDECIDED) { //&#x5982;&#x679C;UNDECIDED&#xFF0C;&#x90A3;&#x4E48;&#x5C31;&#x628A;result&#x8BBE;&#x7F6E;&#x4E3A;COROUTINE_SUSPENDED
       if (RESULT.compareAndSet(this, UNDECIDED, COROUTINE_SUSPENDED)) returnCOROUTINE_SUSPENDED
       result = this.result // reread volatile var
  }
   return when {
       result === RESUMED -> COROUTINE_SUSPENDED // already called continuation, indicate COROUTINE_SUSPENDED upstream
       result is Result.Failure -> throw result.exception
       else -> result // either COROUTINE_SUSPENDED or data <-这里返回白板 } ​ public actual override fun resumewith(result: result<t>) {
       while (true) { // lock-free loop
           val cur = this.result // atomic read&#x3002;&#x4E0D;&#x7406;&#x89E3;&#x8FD9;&#x91CC;&#x7684;&#x5B98;&#x65B9;&#x6CE8;&#x91CA;&#x4E3A;&#x5565;&#x53EB;&#x505A;&#x539F;&#x5B50;&#x8BFB;&#x3002;&#x6211;&#x89C9;&#x5F97; Volatile&#x53EA;&#x80FD;&#x4FDD;&#x8BC1;&#x53EF;&#x89C1;&#x6027;&#x3002;
           when {
             //&#x8FD9;&#x91CC;&#x5982;&#x679C;&#x662F;UNDECIDED &#x5C31;&#x628A; &#x7ED3;&#x679C;&#x9644;&#x4E0A;&#x53BB;&#x3002;
               cur === UNDECIDED -> if (RESULT.compareAndSet(this, UNDECIDED, result.value)) return
             //&#x5982;&#x679C;&#x662F;&#x6302;&#x8D77;&#x72B6;&#x6001;&#xFF0C;&#x5C31;&#x901A;&#x8FC7;resumeWith&#x56DE;&#x8C03;&#x72B6;&#x6001;&#x673A;
               cur === COROUTINE_SUSPENDED -> if (RESULT.compareAndSet(this, COROUTINE_SUSPENDED, RESUMED)){
                   delegate.resumeWith(result)
                   return
              }
               else -> throw IllegalStateException("Already resumed")
          }
      }
  }

</-这里返回白板></any?></safecontinuation<*></t>
val safe = SafeContinuation(c.intercepted())
block(safe)
safe.getOrThrow()

先回顾一下什么叫真正的挂起,就是getOrThrow返回了”白板”,那么什么时候getOrThrow能返回白板?答案就是result被初始化后值没被修改过。那么也就是说resumeWith没有被执行过,即:block(safe)这句代码,block这个被传进来的函数,执行过程中没有调用safe的resumeWith。原理就是这么简单,cas代码保证关键逻辑的原子性与并发安全

继续以Demo-K1为例子, 这里假设hello2运行在一条新的子线程,否则仍然是没有挂起。

{
   thread{
       Thread.sleep(1000)
       it.resume(10086)
  }
}

总结

最后,可以说开启一个协程,就是利用编译器生成一个状态机对象,帮我们把回调代码拍扁,成为同步代码。

Original: https://www.cnblogs.com/BlueSocks/p/16072137.html
Author: BlueSocks
Title: Android中的Coroutine协程原理详解

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/606456/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 软件危机复习

    没有银弹的含义 软件危机:由于软件规模越来越大,软件复杂性越来越高,可靠性问题也越来越突出,传统的个人设计,个人实现的方式不再满足要求,迫切需要改变软件生产方式,提高软件开发效率,…

    Linux 2023年6月8日
    081
  • 关于 Promise 的一些简单理解

    一、ES6 中的 Promise 1、JS 如何解决 异步问题? (1)什么是 同步、异步?同步指的是 需要等待 前一个处理 完成,才会进行 下一个处理。异步指的是 不需要等待 前…

    Linux 2023年6月11日
    0103
  • docker save与docker export实现docker镜像与容器的备份

    本来想写一篇关于docker save/export/commit/load/import之间的关系的文章,后来看了看,已经有很多人写过了,我就不做重复工作了。 参见: docke…

    Linux 2023年6月6日
    097
  • spring-data-redis 2.0 的使用

    在使用Spring Boot2.x运行Redis时,发现百度不到顺手的文档,搞通后发现其实这个过程非常简单和简洁,觉得有必要拿出来分享一下。 Spring Boot2.x 不再使用…

    Linux 2023年5月28日
    0102
  • shell批量创建数据表的一个方法

    #!/bin/bash #批量新建数据表 #删除`符号,具体原因我也没搞懂 for i in {1..30};do mysql 地址 -u账号 -p密码 -e "use …

    Linux 2023年5月28日
    075
  • find 查询命令 & 正则表达式

    今日内容 find 正则表达式 Linux 三剑客之 grep 内容详细 一、find 按名称或属性查询文件 按名称查询 find [查找目录] [参数] [] 通配符 : * 表…

    Linux 2023年5月27日
    083
  • 用 shell 脚本制造连接频繁中断的场景

    问题的提出 最近在准备客户端的新版本,在内部灰度过程中,发现一类奇怪的 dump,通过查看日志和堆栈,可以确定是因为每次连上后台就被后台断开了、导致多次重连后随机发生的崩溃。dum…

    Linux 2023年6月6日
    081
  • docker学习笔记—基本命令

    1、docker start/stop/restart/kill 启动/停止/重启/杀掉容器 实例操作如下: 2、docker run 创建并启动一个新的容器 常用参数如下: 实例…

    Linux 2023年6月8日
    090
  • Centons7最小化安装报错:ping: baidu.com: Name or service not know

    在这之前,centos7最小化安装默认是不能联网的,首先必须切换到root用户,再解决网络问题 一、 切换到root用户 二、 解决网络问题 一、切换到root用户 Linux下切…

    Linux 2023年6月7日
    0148
  • LVS+KeepAlived高可用部署架构

    1 构建高可用集群 1.1 什么是高可用集群 高可用集群(High Availability Cluster,简称HA Cluster),是指以减少服务中断时间为目的得服务器集群技…

    Linux 2023年6月13日
    072
  • 关于树莓派64位操作系统

    用过树莓派的都知道,在烧录操作系统时,官方只提供的32位的系统,这是官方经过测试和验证比较稳定的系统,对于使用4GB或8GB版本大内存树莓派用户来说,通常会将树莓派拿来充当服务器或…

    Linux 2023年5月27日
    0101
  • Docker安装使用及私有仓库搭建

    1 概念 1.1 基本概念 Docker daemon​ 守护进程,运行在宿主机上,用户通过DockerClient客户端Docker命令与Docker daemon交互。Dock…

    Linux 2023年5月27日
    087
  • mac 如何仅安装redis-cli客户端

    brew tap ringohub/redis-cli brew update && brew doctor brew install redis-cli 【注】需…

    Linux 2023年5月28日
    0101
  • openssh升级至7.2

    此处升级操作的原则是保留系统原有ssh服务,新安装高版本ssh服务 1、下载openssh源码包 http://www.openssh.com/portable.html 2、安装…

    Linux 2023年6月14日
    097
  • POJ1475(Pushing Boxes)–bbffss

    假设只有一个箱子。游戏在一个R行C列的由单位格子组成的区域中进行,每一步, 你可以移动到相邻的四个格子中的一个,前提是那个格子是空的;或者,如果你在箱子旁边,你也可以推动箱子前进一…

    Linux 2023年6月7日
    099
  • Kafka部署安装及简单使用

    一、环境准备 1、jdk 8+ 2、zookeeper 3、kafka 说明:在kafka较新版本中已经集成了zookeeper,所以不用单独安装zookeeper,只需要在kaf…

    Linux 2023年6月13日
    097
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球