Kotlin launch原理全面分析

目录
  • 一、协程是如何创建的
    • 协程启动的基础API
  • 二、launch 是如何启动协程的

一、协程是如何创建的

launch、async 可以创建、启动新的协程,那么协程到底是如何创建的?

  runBlocking {
        println(Thread.currentThread().name)
        launch {
            println(Thread.currentThread().name)
            delay(100L)
        }
        Thread.sleep(1000L)
    }
Log
main @coroutine#1
main @coroutine#2
Process finished with exit code 0

runBlocking{} 启动了第一个协程,launch{} 启动了第二个协程。

协程启动的基础API

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

createCoroutine{}、startCoroutine{}就是 Kotlin 协程当中最基础的两个创建协程的 API。启动协程有三种常见的方式:launch、runBlocking、async。它们其实属于协程中间层提供的 API,而它们的底层都调用了“基础层”的协程 API。

createCoroutine{}、startCoroutine{}是扩展函数,其扩展接收者类型是一个函数类型:suspend () -> T,代表了“无参数,返回值为 T 的挂起函数或者 Lambda”。而对于函数本身,它们两个都接收一个 Continuation<T> 类型的参数,其中一个函数,还会返回一个 Continuation<Unit> 类型的返回值。

val block = suspend {
    println("Hello")
    delay(1000L)
    println("World!")
    "Result"
}
fun testLaunch2() {
    val continuation = object : Continuation<String> {
        override val context: CoroutineContext
            get() = EmptyCoroutineContext
        override fun resumeWith(result: Result<String>) {
            println("Result:" + result.getOrNull())
        }
    }
    block.startCoroutine(continuation)
}
fun main() {
    testLaunch2()
    Thread.sleep(2000L)
}
Log
Hello
World!
Result:Result
Process finished with exit code 0

类型为suspend () -> T的函数或者Lambda 表达式可以用 block.startCoroutine() 来启动协程了。

Continuation 有两种用法,一种是在实现挂起函数的时候,用于传递挂起函数的执行结果;另一种是在调用挂起函数的时候,以匿名内部类的方式,用于接收挂起函数的执行结果。

使用 createCoroutine() 这个方法其实上面代码的逻辑:

fun testLaunch3() {
    val continuation = object : Continuation<String> {
        override val context: CoroutineContext
            get() = EmptyCoroutineContext
        override fun resumeWith(result: Result<String>) {
            println("Result:" + result.getOrNull())
        }
    }
    val coroutinue = block.createCoroutine(continuation)
    coroutinue.resume(Unit)
}
val block = suspend {
    println("Hello")
    delay(1000L)
    println("World!")
    "Result"
}
fun main() {
    testLaunch3()
    Thread.sleep(2000L)
}
Log
Hello
World!
Result:Result
Process finished with exit code 0

createCoroutine() 创建一个协程,先不启动。调用 resume() 才能启动。createCoroutine()、startCoroutine() 的源代码差别也并不大,只是前者没有调用 resume(),而后者调用了 resume()。startCoroutine() 之所以可以创建并同时启动协程的原因就在于,它在源码中直接调用了 resume(Unit)。

将 startCoroutine()转换为Java:

package com.example.myapplication.testcoroutinue;
import kotlin.Metadata;
import kotlin.Result;
import kotlin.ResultKt;
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.ContinuationKt;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.EmptyCoroutineContext;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.internal.Intrinsics;
import kotlinx.coroutines.DelayKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Metadata(
   mv = {1, 6, 0},
   k = 2,
   d1 = {"\u0000\u001e\n\u0000\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\u0010\u000e\n\u0002\u0010\u0000\n\u0002\b\u0004\n\u0002\u0010\u0002\n\u0002\b\u0002\u001a\u0006\u0010\b\u001a\u00020\t\u001a\u0006\u0010\n\u001a\u00020\t\",\u0010\u0000\u001a\u0018\b\u0001\u0012\n\u0012\b\u0012\u0004\u0012\u00020\u00030\u0002\u0012\u0006\u0012\u0004\u0018\u00010\u00040\u0001ø\u0001\u0000¢\u0006\n\n\u0002\u0010\u0007\u001a\u0004\b\u0005\u0010\u0006\u0082\u0002\u0004\n\u0002\b\u0019¨\u0006\u000b"},
   d2 = {"block", "Lkotlin/Function1;", "Lkotlin/coroutines/Continuation;", "", "", "getBlock", "()Lkotlin/jvm/functions/Function1;", "Lkotlin/jvm/functions/Function1;", "main", "", "testLaunch2", "My_Application.app.main"}
)
public final class TestCoroutinue888Kt {
// Kotlin 为 block 变量生成的静态变量
   @NotNull
   private static final Function1 block;
   public static final void main() {
      testLaunch2();
      Thread.sleep(2000L);
   }
   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
    // Kotlin 为 block 变量生成的静态变量以及方法
   @NotNull
   public static final Function1 getBlock() {
      return block;
   }
   public static final void testLaunch2() {
//continuation 变量对应的匿名内部类
      <undefinedtype> continuation = new Continuation() {
         @NotNull
         public CoroutineContext getContext() {
            return (CoroutineContext)EmptyCoroutineContext.INSTANCE;
         }
         public void resumeWith(@NotNull Object result) {
            String var2 = "Result:" + (String)(Result.isFailure-impl(result) ? null : result);
            System.out.println(var2);
         }
      };
//block.startCoroutine(continuation) 转换成了ContinuationKt.startCoroutine(block, (Continuation)continuation)
      ContinuationKt.startCoroutine(block, (Continuation)continuation);
   }
   static {
    //实现了 Continuation 接口
      Function1 var0 = (Function1)(new Function1((Continuation)null) {
         int label;
//invokeSuspend()为协程状态机逻辑
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            String var2;
            switch(this.label) {
            case 0:
               ResultKt.throwOnFailure($result);
               var2 = "Hello";
               System.out.println(var2);
               this.label = 1;
               if (DelayKt.delay(1000L, this) == var3) {
                  return var3;
               }
               break;
            case 1:
               ResultKt.throwOnFailure($result);
               break;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }
            var2 = "World!";
            System.out.println(var2);
            return "Result";
         }
         @NotNull
         public final Continuation create(@NotNull Continuation completion) {
            Intrinsics.checkNotNullParameter(completion, "completion");
            Function1 var2 = new <anonymous constructor>(completion);
            return var2;
         }
         public final Object invoke(Object var1) {
            return ((<undefinedtype>)this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);
         }
      });
      block = var0;
   }
}
public fun <T> (suspend () -> T).startCoroutine(
    completion: Continuation<T>
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}

在 startCoroutine() 当中,首先会调用 createCoroutineUnintercepted() 方法。

public expect fun <T> (suspend () -> T).createCoroutineUnintercepted(
    completion: Continuation<T>
): Continuation<Unit>

代码中的 expect,一种声明,由于 Kotlin 是面向多个平台的,具体的实现,就需要在特定的平台实现。

public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
    completion: Continuation<T>
): Continuation<Unit> {
    val probeCompletion = probeCoroutineCreated(completion)
    return if (this is BaseContinuationImpl)
        create(probeCompletion)
    else
        createCoroutineFromSuspendFunction(probeCompletion) {
            (this as Function1<Continuation<T>, Any?>).invoke(it)
        }
}

actual,代表了 createCoroutineUnintercepted() 在 JVM 平台的实现。

createCoroutineUnintercepted() 是一个扩展函数,this代表了 block 变量。(this is BaseContinuationImpl) 条件为ture,就会调用 create(probeCompletion)。

public open fun create(completion: Continuation<*>): Continuation<Unit> {
    throw UnsupportedOperationException("create(Continuation) has not been overridden")
}

在默认情况下,这个 create() 方法是会抛出异常的。

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

返回了Continuation 对象。

public fun <T> (suspend () -> T).startCoroutine(
    completion: Continuation<T>
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}

intercepted() 在JVM 实现如下:

public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
    (this as? ContinuationImpl)?.intercepted() ?: this

将 Continuation 强转成了 ContinuationImpl,调用了它的 intercepted()。

ContinuationImpl 的源代码:

internal abstract class ContinuationImpl(
    completion: Continuation<Any?>?,
    private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
    @Transient
    private var intercepted: Continuation<Any?>? = null
    public fun intercepted(): Continuation<Any?> =
        intercepted
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also { intercepted = it }
}

通过 ContinuationInterceptor,对 Continuation 进行拦截,从而将程序的执行逻辑派发到特定的线程之上。

resume(Unit):

public fun <T> (suspend () -> T).startCoroutine(
    completion: Continuation<T>
) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}

resume(Unit),作用其实就相当于启动了协程。

二、launch 是如何启动协程的

fun main() {
    testLaunch11()
    Thread.sleep(2000L)
}
fun testLaunch11() {
    val coroutineScope = CoroutineScope(Job())
    coroutineScope.launch {
        println("Hello")
        delay(1000L)
        println("World!")
    }
}
Log
Hello
World!
Process finished with exit code 0

转Java

package com.example.myapplication.testcoroutinue;
import kotlin.Metadata;
import kotlin.ResultKt;
import kotlin.Unit;
import kotlin.coroutines.Continuation;
import kotlin.coroutines.CoroutineContext;
import kotlin.coroutines.intrinsics.IntrinsicsKt;
import kotlin.jvm.functions.Function2;
import kotlin.jvm.internal.Intrinsics;
import kotlinx.coroutines.BuildersKt;
import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.CoroutineScopeKt;
import kotlinx.coroutines.CoroutineStart;
import kotlinx.coroutines.DelayKt;
import kotlinx.coroutines.Job;
import kotlinx.coroutines.JobKt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@Metadata(
   mv = {1, 6, 0},
   k = 2,
   d1 = {"\u0000\n\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0002\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u001a\u0006\u0010\u0002\u001a\u00020\u0001¨\u0006\u0003"},
   d2 = {"main", "", "testLaunch11", "My_Application.app.main"}
)
public final class TestCoroutinue999Kt {
   public static final void main() {
      testLaunch11();
      Thread.sleep(2000L);
   }
   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
   public static final void testLaunch11() {
      CoroutineScope coroutineScope = CoroutineScopeKt.CoroutineScope((CoroutineContext)JobKt.Job$default((Job)null, 1, (Object)null));
//对应 launch 当中的 Lambda。
      BuildersKt.launch$default(coroutineScope, (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
         int label;
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            String var2;
            switch(this.label) {
            case 0:
               ResultKt.throwOnFailure($result);
               var2 = "Hello";
               System.out.println(var2);
               this.label = 1;
               if (DelayKt.delay(1000L, this) == var3) {
                  return var3;
               }
               break;
            case 1:
               ResultKt.throwOnFailure($result);
               break;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }
            var2 = "World!";
            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);
         }
      }), 3, (Object)null);
   }
}

launch源码

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    //launch 会根据传入的 CoroutineContext 创建出新的 Context。
    val newContext = newCoroutineContext(context)
    //launch 会根据传入的启动模式来创建对应的协程对象。这里有两种,一种是标准的,一种是懒加载的。
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    //启动协程。
    coroutine.start(start, coroutine, block)
    return coroutine
}

coroutine.start() :

public abstract class AbstractCoroutine<in T>(
    parentContext: CoroutineContext,
    initParentJob: Boolean,
    active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
    public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
        start(block, receiver, this)
    }
}

AbstractCoroutine.kt 对应协程的抽象逻辑。AbstractCoroutine 的start() 方法,用于启动协程。

public enum class CoroutineStart {
    public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>): Unit =
        when (this) {
            DEFAULT -> block.startCoroutineCancellable(completion)
            ATOMIC -> block.startCoroutine(completion)
            UNDISPATCHED -> block.startCoroutineUndispatched(completion)
            LAZY -> Unit // will start lazily
        }
}

start(block, receiver, this),进入 CoroutineStart.invoke()。

invoke() 方法当中,根据 launch 传入的启动模式,以不同的方式启动协程。当启动模式是 ATOMIC 的时候,就会调用 block.startCoroutine(completion)。startCoroutineUndispatched(completion) 和 startCoroutineCancellable(completion),只是在 startCoroutine() 的基础上增加了一些额外的功能而已。前者代表启动协程以后就不会被分发,后者代表启动以后可以响应取消。

startCoroutineCancellable(completion)

public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
    createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith(Result.success(Unit))
}
public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
    completion: Continuation<T>
): Continuation<Unit> {
    val probeCompletion = probeCoroutineCreated(completion)
    return if (this is BaseContinuationImpl)
        create(probeCompletion)
    else
        createCoroutineFromSuspendFunction(probeCompletion) {
            (this as Function1<Continuation<T>, Any?>).invoke(it)
        }
}

startCoroutineCancellable() 的源代码,会调用 createCoroutineUnintercepted(),然后调用 create(probeCompletion),然后最终会调用create() 方法。launch 这个 API,只是对协程的基础元素 startCoroutine() 等方法进行了一些封装而已。

到此这篇关于Kotlin launch原理全面分析的文章就介绍到这了,更多相关Kotlin launch内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Kotlin协程之Flow基础原理示例解析

    目录 引言 一.Flow的创建 二.Flow的消费 1.SafeFlow类 2.AbstractFlow类 3. SafeCollector类 4.消费过程中的挂起 引言 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.collect { delay(1000) withContext(Dispatchers.IO) { Log.d("liduo", "$it") } Log.d(&qu

  • Kotlin协程launch启动流程原理详解

    目录 1.launch启动流程 反编译后的Java代码 2.协程是如何被启动的 1.launch启动流程 已知协程的启动方式之一是Globalscope.launch,那么Globalscope.launch的流程是怎样的呢,直接进入launch的源码开始看起. fun main() { coroutineTest() Thread.sleep(2000L) } val block = suspend { println("Hello") delay(1000L) println(&q

  • Kotlin 协程的取消机制详细解读

    目录 引言 协程的状态 取消协程的用法 协程取消的有效性 如何写出可以取消的代码 在 finally 中释放资源 使用不可取消的 block CancellationException 超时取消 异步的超时和资源 取消检查的底层原理 引言 在 Java 语言中提供了线程中断的能力,但并不是所有的线程都可以中断的,因为 interrupt 方法并不是真正的终止线程,而是将一个标志位标记为中断状态,当运行到下一次中断标志位检查时,才能触发终止线程. 但无论如何,终止线程是一个糟糕的方案,因为在线程的

  • Kotlin协程launch原理详解

    目录 正文 launch使用 launch原理 CoroutineStart中找invoke方法 startCoroutineCancellable逻辑 小结 正文 launch我们经常用,今天来看看它是什么原理. 建议: 食用本篇文章之前记得先食用Kotlin协程之createCoroutine和startCoroutine launch使用 launch我们应该很熟悉了,随便举个例子: fun main() { val coroutineScope = CoroutineScope(Job(

  • Kotlin协程的基础与使用示例详解

    目录 一.协程概述 1.概念 2.特点 3.原理 1)续体传递 2)状态机 二.协程基础 1.协程的上下文 2.协程的作用域 3.协程调度器 4.协程的启动模式 5.协程的生命周期 1)协程状态的转换 2)状态标识的变化 三.协程使用 1.协程的启动 1)runBlocking方法 2)launch方法 3)async方法 4)suspend关键字 5)withContext方法 6)suspend方法 2.协程间通信 1)Channel 2)Channel的容量 3)produce方法与act

  • kotlin 协程上下文异常处理详解

    目录 引言 一.协程上下文 1.CoroutineContext 2.CorountineScope 3.子协程继承父协程 二.协程的异常传递 1.协程的异常传播 2.不同上下文(没有继承关系)之间协程异常会怎么样? 3.向用户暴露异常 三.协程的异常处理 使用SupervisorJob 异常捕获器CoroutineExceptionHandler Android中全局异常的处理 引言 从前面我们可以大致了解了协程的玩法,如果一个协程中使用子协程,那么该协程会等待子协程执行结束后才真正退出,而达

  • Java中的Kotlin 内部类原理

    目录 Java 中的内部类 OutterJava.class InnJava.class Kotlin 中的内部类 总结 Java 中的内部类 这是一个 Java 内部类的简单实现: public class OutterJava {    private void printOut() {        System.out.println("AAA");   } ​    class InnJava {        public void printInn() {        

  • 详解Java线程池和Executor原理的分析

    详解Java线程池和Executor原理的分析 线程池作用与基本知识 在开始之前,我们先来讨论下"线程池"这个概念."线程池",顾名思义就是一个线程缓存.它是一个或者多个线程的集合,用户可以把需要执行的任务简单地扔给线程池,而不用过多的纠结与执行的细节.那么线程池有哪些作用?或者说与直接用Thread相比,有什么优势?我简单总结了以下几点: 减小线程创建和销毁带来的消耗 对于Java Thread的实现,我在前面的一篇blog中进行了分析.Java Thread与内

  • python爬虫Scrapy框架:媒体管道原理学习分析

    目录 一.媒体管道 1.1.媒体管道的特性 媒体管道实现了以下特性: 图像管道具有一些额外的图像处理功能: 1.2.媒体管道的设置 二.ImagesPipeline类简介 三.小案例:使用图片管道爬取百度图片 3.1.spider文件 3.2.items文件 3.3.settings文件 3.4.pipelines文件 一.媒体管道 1.1.媒体管道的特性 媒体管道实现了以下特性: 避免重新下载最近下载的媒体 指定存储位置(文件系统目录,Amazon S3 bucket,谷歌云存储bucket)

  • 建模UML用例图使用原理总结分析

    目录 用例图所包含的元素如下 1.参与者(Actor) 2.用例(Use Case) 3. 子系统(Subsystem) 4.关系 a. 关联(Association) b. 泛化(Inheritance) c. 包含(Include) d. 扩展(Extend) e. 依赖(Dependency) 5. 项目(Artifact) 6. 注释(Comment) 牢骚: 用例图主要用来描述"用户.需求.系统功能单元"之间的关系.它展示了一个外部用户能够观察到的系统功能模型图. [用途]:

  • Java与Kotlin互调原理

    目录 一.Kt调用-Java参数非null的处理 @NotNull Kotlin中调用 二.Kt调用-Java中使用kt关键字声明的变量和方法 Kotlin中调用 三.Kt调用Java-SAM转换 在Kotlin中调用 四.Kt中禁止Java调用某方法 @JvmSynthetic 五.Java调用Kt-扩展函数 @file:JvmName(“xx”) 六.Java调用kt-成员变量 @JvmField Kotlin @get:JvmName,@set:JvmName Kotlin 七.Java调

  • Java与Kotlin互调原理讲解

    目录 一.Kt调用-Java参数非null的处理 @NotNull Kotlin中调用 二.Kt调用-Java中使用kt关键字声明的变量和方法 Kotlin中调用 三.Kt调用Java-SAM转换 在Kotlin中调用 四.Kt中禁止Java调用某方法 @JvmSynthetic 五.Java调用Kt-扩展函数 @file:JvmName(“xx”) 六.Java调用kt-成员变量 @JvmField Kotlin @get:JvmName,@set:JvmName Kotlin 七.Java调

  • MySQL的主从复制原理详细分析

    目录 前言 一.主从复制概念 二.读写分离的概念 三.主库和从库 1. 主库 2. 从库 四.主从复制的流程 五.主从复制效果展示 前言 在实际生产环境中,如果对mysql数据库的读和写都在一台数据库服务器中操作,无论是在安全性.高可用性,还是高并发等各个方面都是不能满足实际需求的,一般要通过主从复制的方式来同步数据,再通过读写分离来提升数据库的并发负载能力. 一.主从复制概念 主从复制是MySQL提供的基本的技术,主从复制的流程:binlog二进制日志(除了查询其他的更改相关的操作都会记录在b

  • SpringBoot自动配置特点与原理详细分析

    目录 一.SpringBoot是什么 二.SpringBoot的特点(核心功能) 三.SpringBoot的自动配置原理 1. @SpringBootApplication 2. @SpringBootConfiguration 3. @EnableAutoConfiguration 4. @ComponentScan 四.核心原理图 五.常用的Conditional注解 一.SpringBoot是什么 Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Sprin

  • Kotlin标准库函数使用分析及介绍

    目录 1.apply 函数 2.let 函数 3.run函数 4.with 函数 5.also 6.takeIf 7.takeUnless 1.apply 函数 apply函数可以看做是一个配置函数.针对apply函数的调用者做一些配置,并把调用者返回. 示例:下面apply的调用者是file,调用完之后,返回的还是file,并在apply函数中,针对file做了一些配置. val file = File("d:\\hello.txt").apply { setWritable(tru

随机推荐