Kotlin 协程源码解析

本文深入探讨Kotlin协程的内部原理,包括什么是协程、编译器如何通过CPS实现异步调用同步化、协程上下文切换的详细步骤,以及为何协程执行效率优于线程。通过分析`launch`、`resumeWith`、`suspendInvoke`等关键方法,揭示协程的挂起与恢复机制。同时,文章讨论了结构化并发的概念,解释了`ScopeCoroutine`如何实现任务的统一管理。通过对Kotlin协程源码的剖析,帮助开发者更深入理解协程的工作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文适合有协程使用基础并想了解内部原理的开发者

项目地址:kotlinx.coroutines, 分析的版本: v1.3
本文结构

1、什么协程(Coroutines)
2、编译器实现协程的异步操作的同步调用
3、协程上下文切换
4、协程执行效率要高于线程/线程池
5、结构化并发原理
6、总结

一、什么协程(Coroutines)

看下Android文档对协程的定义:协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念。

二、编译器实现协程的异步操作的同步调用。

2.1 CPS(Continuation Passing Style)

我们先看一个经典的网络请求并更新UI的例子

private val uiHandler = Handler(Looper.getMainLooper())

fun fetchDocs() {
   
   
    get("https://developer.android.com") {
   
    result ->
        show(result)
    }
}

fun get(url: String, onSuccess: (String) -> Unit) {
   
   
    Thread {
   
   
        print("进行网络请求 get = $url")
        onSuccess("Docs Content")
    }.start()
}

fun show(content: String) {
   
   
    uiHandler.post {
   
    print("显示dialog $content") }
}

从上面例子可以看到我们需要创建子线程进行网络请求,然后通过回调调用show(),show()里面uiHandler切换到UI线程更新UI。能看出我们通过回调来返回异步操作结果。

我们看看经过协程改造后的例子

fun fetchDocs() {
   
                                
    CoroutineScope(Dispatchers.Main).launch {
   
     // Dispatchers.Main
        val result = get("https://developer.android.com") // Dispatchers.IO for `get`
        show(result)                                      // Dispatchers.Main
    }
}

suspend fun get(url: String) = withContext(Dispatchers.IO) {
   
   
    print("进行网络请求 get = $url")
    return@withContext "success"
}

fun show(content: String) {
   
   
    print("显示dialog $content")
}

我们看到get()的回调不见了,使用withContext(Dispatchers.IO)指定在IO线程执行,并同步方式的返回请求结果,show()将结果更新到UI上。

协程使用了什么魔法把异步的调用变成同步调用呢?

我们把上面代码反编译成Java代码,看看他施了什么魔法,代码如下

public final class CoroutineTest2Kt {
   
   
   public static final void fetchDocs() {
   
   
      BuildersKt.launch$default(CoroutineScopeKt.CoroutineScope(Dispatchers.getMain()), null, null, new Function2((Continuation)null) {
   
   
         private CoroutineScope p$;
         Object L$0;
         // 初始状态 label = 0
         int label;

         // 该调用在Dispatchers.Main
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
   
   
            Object var4 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            Object var10000;
            CoroutineScope $this$launch;
            switch(this.label) {
   
   
            case 0:
               ResultKt.throwOnFailure($result);
               $this$launch = this.p$;
               this.L$0 = $this$launch;
               // 将label切换为1
               this.label = 1;
               // 把本continuation传给get(),并挂起自身continuation
               var10000 = CoroutineTest2Kt.get("https://developer.android.com", this);
               if (var10000 == var4) {
   
   
                  return var4;
               }
               break;
            case 1:
               $this$launch = (CoroutineScope)this.L$0;
               ResultKt.throwOnFailure($result);
               var10000 = $result;
               break;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }
						
            // resumeWhite()重新开始,继续执行,在UI显示
            String result = (String)var10000;
            CoroutineTest2Kt.show(result);
            return Unit.INSTANCE;
         }

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

         public final Object invoke(Object var1, Continuation var2) {
   
   
            return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
         }
      }), 3, (Object)null);
   }

   @Nullable
   public static final Object get(@NotNull final String url, @NotNull Continuation $completion) {
   
   
      return BuildersKt.withContext((CoroutineContext)Dispatchers.getIO(), (Function2)(new Function2((Continuation)null) {
   
   
         private CoroutineScope p$;
         int label;
         
         // 该调用在Dispatchers.IO
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
   
   
            Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            switch(this.label) {
   
   
            case 0:
               // 在IO中处理数据
               ResultKt.throwOnFailure($result);
               CoroutineScope $this$withContext = this.p$;
               String var3 = "进行网络请求 get = " + url;
               boolean var4 = false;
               System.out.print(var3);
               // 返回处理成功
               return "success";
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }
         }

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

         public final Object invoke(Object var1, Object var2) {
   
   
            return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
         }
      }), $completion);
   
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值