协程原理再议

简介: 协程原理再议

协程再议

关于协程的一些理解

协程挂起让异步代码可以像同步代码一样调用,但其本质还是同步,即协程体中的代码其实是同步。

因为协程也只是对线程池的封装,所以需要了解些线程的一些知识。线程本身已经有的协程也会有,但是协程有的线程不一定有

编译器会为每一个挂起函数生成一个匿名内部类,其继承SuspendLabmba类重写其invokeSuspend方法,这个方法里面即为协程体的代码【大致内容请先了解】

编译器会对协程体中的挂起函数和普通函数进行切割,切割时进行label的自增来保证之后代码的执行顺序,即协程保证运行顺序的本质。【对比线程:进行PC程序计数器的控制来恢复执行】

协程体中会添加一个label字段,标识接下来该运行协程体中的哪行代码【即协程如何知道自己执行到哪步是用这个label完成的】(对比线程:线程存储下一个代码指令是用PC计数器来做的)

协程体中的数据,看过之前那篇文章的人应该大致了解挂起函数其实就是匿名内部类,数据是保存在栈帧中的,(对比线程:也是通过栈帧中的局部变量表和操作数栈来存储数据)

协程体中碰到挂起函数会直接返回,等待挂起函数通知

当我们调用挂起函数时都会传入一个Continuation,挂起函数执行完正常退出或者抛异常退出这个时候外面的协程需要知道这个信息,怎么通知呢?通过Continuation的resumewith方法,这个方法会再次调用invokeSuspend取出label来保证之后执行代码的顺序,即协程自动恢复运行的本质

(对比线程:方法正常执行完成之后有两种情况1、正常结束,2.异常退出。其里面的PC计数器保证之后执行的代码顺序,也就是说协程在原本线程之上又加了一层控制)

协程体中可以在开启一个协程,也就是协程具有父子关系的本质。在协程伊始的时候会默认给一些默认数据(包括协程体运行在哪个线程即调度器其通过拦截器实现,执行状态检测用到的Job,拦截器等等)这些数据保存在协程的上下文中,

当在协程体中又开启了一个协程时,其会获取父协程的上下文进行和自己的合并作为自己的上下文。

【简而言之,和协程本身有关的数据保存在上下文中,和业务有关的代码会放在匿名内部类中。】

基础框架层源码分析

val createCoroutine = suspend {
                //挂起函数代码
            }.createCoroutine(object : Continuation<Unit> {
                override val context: CoroutineContext
                    get() = TODO("Not yet implemented")
                override fun resumeWith(result: Result<Unit>) {
          //可从result中获取返回值或异常
                }
            })
   createCoroutine.resume(Unit)

1.编译器会对挂起函数做处理,让他继承SuspendCorunting,重写其invokeSuspend方法,方法体为挂起函数中的代码

2.当手动调用createCoroutine返回值的resume方法的时候,会调用到resumeWith函数,其里面会调用invokeSuspend也就是挂起函数体里面的代码,之后两种情况:

3.碰到普通函数直接执行;

碰到挂起函数传入Continuation并直接返回一个标识代表其为挂起函数,这个时候协程直接退出,之后挂起函数执行完调用Continuation的resumeWith继续执行方法体代码【通过label确定顺序】

4.在invokeSuepend中可以通过result获取挂起函数执行的结果:异常或者返回值。进行对应处理。

实战分析

class ExampleUnitTest {
    @Test
    fun addition_isCorrect() {
        GlobalScope.launch {
            println("挂起点1开始")
            delay(1000)         //挂起点1
            println("挂起点1结束")
            hello()             //挂起点2
            println("挂起点2结束")
            delay(1000)         //挂起点3
            println("挂起点3结束")
            word()              //挂起点4
        }
    }
    //挂起函数,编译器默认传入Continuation
    suspend fun hello(){
        //再次挂起
        withContext(Dispatchers.IO){
            delay(1000)
            println("hello")
        }
    }
    suspend fun word(){
        withContext(Dispatchers.IO){
            delay(1000)
            println("word")
        }
    }

反编译后代码分析

public final class ExampleUnitTest {
   @Test
   public final void addition_isCorrect() {
      Assert.assertEquals(4L, 4L);
      BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
         int label;
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            ExampleUnitTest var10000;
            String var2;
            boolean var3;
            Object var4;
            //对应的标志位
            label34: {
               label33: {
                  var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                  switch(this.label) {
                  case 0:
                     ResultKt.throwOnFailure($result);
                     var2 = "挂起点1开始";
                     var3 = false;
                     System.out.println(var2);
                     this.label = 1;
                     //根据返回值判断是否为挂起函数,如果为挂起函数直接返回
                     // 挂起函数会持有Continuation,挂起函数执行后调用其resumeWith方法,之后就会回到invokeSuspend中
                     if (DelayKt.delay(1000L, this) == var4) {
                        //协程退出
                        return var4;
                     }
                     break;
                  case 1:
                     //检测上一步也就是delay是否发生异常或者失败,接着执行-----》挂起点1结束
                     ResultKt.throwOnFailure($result);
                     break;
                  case 2:
                     //检测上一步也就是hello是否发生异常或者失败,接着执行-----》挂起点2结束
                     ResultKt.throwOnFailure($result);
                     break label33;
                  case 3:
                     //检测上一步也就是delay是否发生异常或者失败,接着执行-----》挂起点3结束
                     ResultKt.throwOnFailure($result);
                     break label34;
                  case 4:
                     ResultKt.throwOnFailure($result);
                     return Unit.INSTANCE;
                  default:
                     throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                  }
                  var2 = "挂起点1结束";
                  var3 = false;
                  //打印
                  System.out.println(var2);
                  var10000 = ExampleUnitTest.this;
                  //label增加,表示上一步执行完成,指向下一个该执行的函数体
                  this.label = 2;
                  //检测hello是否为挂起函数
                  if (var10000.hello(this) == var4) {
                     //协程退出
                     return var4;
                  }
               }
               //退出label33后执行的函数
               var2 = "挂起点2结束";
               var3 = false;
               System.out.println(var2);
               //再次更正
               this.label = 3;
               //检测是否为挂起函数
               if (DelayKt.delay(1000L, this) == var4) {
                  //直接退出协程
                  return var4;
               }
            }
            //退出label34后执行的函数
            var2 = "挂起点3结束";
            var3 = false;
            System.out.println(var2);
            var10000 = ExampleUnitTest.this;
            this.label = 4;
             //检测是否为挂起函数
            if (var10000.word(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);
         }
      }), 3, (Object)null);
   }
}

看完上面的反编译解析会发现还差了两个方法,没错,接下来就是自定的挂起函数的反编译代码,根据上面的分析请读者亲自分析下接下来的这两个函数

@Nullable
public final Object hello(@NotNull Continuation $completion) {
   Object var10000 = BuildersKt.withContext((CoroutineContext)Dispatchers.getIO(), (Function2)(new Function2((Continuation)null) {
      int label;
      @Nullable
      public final Object invokeSuspend(@NotNull Object $result) {
      Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      switch(this.label) {
         case 0:
         ResultKt.throwOnFailure($result);
         this.label = 1;
         if (DelayKt.delay(1000L, this) == var4) {
            return var4;
         }
         break;
         case 1:
         ResultKt.throwOnFailure($result);
         break;
         default:
         throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
      }
      String var2 = "hello";
      boolean var3 = false;
      System.out.println(var2);
      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);
   }
   }), $completion);
   return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : Unit.INSTANCE;
}
@Nullable
public final Object word(@NotNull Continuation $completion) {
   Object var10000 = BuildersKt.withContext((CoroutineContext)Dispatchers.getIO(), (Function2)(new Function2((Continuation)null) {
      int label;
      @Nullable
      public final Object invokeSuspend(@NotNull Object $result) {
      Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
      switch(this.label) {
         case 0:
         ResultKt.throwOnFailure($result);
         this.label = 1;
         if (DelayKt.delay(1000L, this) == var4) {
            return var4;
         }
         break;
         case 1:
         ResultKt.throwOnFailure($result);
         break;
         default:
         throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
      }
      String var2 = "word";
      boolean var3 = false;
      System.out.println(var2);
      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);
   }
   }), $completion);
   return var10000 == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? var10000 : Unit.INSTANCE;
}

总而言之,言而总之。还是上一篇博客总结的道理,就是不断的invokeSuspend标记标志位和挂起函数调用resumeWith。



目录
相关文章
|
6月前
|
API 调度
2.3.1 协程设计原理与汇编实现
2.3.1 协程设计原理与汇编实现
|
3月前
|
存储 Linux 调度
协程(coroutine)的原理和使用
协程(coroutine)的原理和使用
|
15天前
|
存储 安全 测试技术
GoLang协程Goroutiney原理与GMP模型详解
本文详细介绍了Go语言中的Goroutine及其背后的GMP模型。Goroutine是Go语言中的一种轻量级线程,由Go运行时管理,支持高效的并发编程。文章讲解了Goroutine的创建、调度、上下文切换和栈管理等核心机制,并通过示例代码展示了如何使用Goroutine。GMP模型(Goroutine、Processor、Machine)是Go运行时调度Goroutine的基础,通过合理的调度策略,实现了高并发和高性能的程序执行。
76 29
|
13天前
|
负载均衡 算法 Go
GoLang协程Goroutiney原理与GMP模型详解
【11月更文挑战第4天】Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理,创建和销毁开销小,适合高并发场景。其调度采用非抢占式和协作式多任务处理结合的方式。GMP 模型包括 G(Goroutine)、M(系统线程)和 P(逻辑处理器),通过工作窃取算法实现负载均衡,确保高效利用系统资源。
|
2月前
|
调度 Python
揭秘Python并发编程核心:深入理解协程与异步函数的工作原理
在Python异步编程领域,协程与异步函数成为处理并发任务的关键工具。协程(微线程)比操作系统线程更轻量级,通过`async def`定义并在遇到`await`表达式时暂停执行。异步函数利用`await`实现任务间的切换。事件循环作为异步编程的核心,负责调度任务;`asyncio`库提供了事件循环的管理。Future对象则优雅地处理异步结果。掌握这些概念,可使代码更高效、简洁且易于维护。
26 1
|
1月前
|
数据采集 调度 Python
Python编程异步爬虫——协程的基本原理(一)
Python编程异步爬虫——协程的基本原理(一)
|
1月前
|
数据采集 Python
Python编程异步爬虫——协程的基本原理(二)
Python编程异步爬虫——协程的基本原理(二)
|
1月前
|
存储 前端开发 rax
协程设计与原理(二)
协程设计与原理(二)
16 0
|
1月前
|
Java Linux Go
协程的设计原理(一)
协程的设计原理(一)
30 0
|
4月前
|
调度 Python
揭秘Python并发编程核心:深入理解协程与异步函数的工作原理
【7月更文挑战第15天】Python异步编程借助协程和async/await提升并发性能,减少资源消耗。协程(async def)轻量级、用户态,便于控制。事件循环,如`asyncio.get_event_loop()`,调度任务执行。异步函数内的await关键词用于协程间切换。回调和Future对象简化异步结果处理。理解这些概念能写出高效、易维护的异步代码。
58 2