美文网首页
Kotlin进阶-协程基础

Kotlin进阶-协程基础

作者: 牵着蚂蚁遛弯儿 | 来源:发表于2021-11-13 21:50 被阅读0次

    1.什么是协程

    我们先来看以下程序

    开启一个协程来打印“World!”,在主线程中打印“Hello,”,主线程打印结束后阻塞2秒等待协程打印

    image.png

    运行结果:
    Hello,
    World!

    我们分别来看下协程和主线程的名称

    image.png

    运行结果:
    Hello,
    main
    World!
    DefaultDispatcher-worker-1

    可以看到,主线程的线程名称为main,而协程的线程名称为DefaultDispatcher-worker-1,说明Kotlin协程在运行时创建了一个Java中的子线程

    那什么是协程呢?
    本质上,协程就是轻量级的线程。Kotlin提供的Java的线程框架

    分析上面的代码:
    GlobalScope:作用域构建器,后续会详细讲解。
    delay: delay 是一个特殊的挂起函数 ,它不会造成线程阻塞,但是会挂起协程,并且只能在协程中使用。

    2.挂起和阻塞

    挂起:一般是主动的,由系统或程序发出,阻塞当前作用域,但所在线程还在执行(执行其他协程作用域)。(不是放CPU,可能释放内存,放在外存)
    阻塞:一般是被动的,在抢占资源中得不到资源,被动的挂起在内存,等待某种资源或信号量(即有了资源)将它唤醒。(释放CPU,不是放内存)

    delay挂起函数的定义:public suspend fun delay(timeMillis: Long)
    使用suspend关键字修饰,意思是挂起,作为用户,阻塞和挂起效果是一样的,都是停止运行。

    也可以使用runBlocking来实现阻塞主线程

    image.png

    上面代码结果是相似的,但是这些代码只使用了非阻塞的函数delay,并没有使用sleep。
    调用了runBlocking的主线程会一直阻塞直到runBlocking内部的协程执行完毕。

    这个示例可以使用更合乎习惯用法的方式重写,使用 runBlocking 来包装 main 函数的执行

    image.png

    以上把主线程包装成了一个Kotlin的协程

    3.等待一个作业

    延迟一段时间来等待另一个协程运行并不是一个好的选择。让我们显式(以非阻塞方式)等待所启动的后台 Job 执行结束:

    image.png

    现在,结果仍然相同,但是主协程与后台作业的持续时间没有任何关系了。好多了。

    4.结构化的并发

    在我们的示例中,我们使用 runBlocking 协程构建器将 main 函数转换为协程。包括 runBlocking 在内的 每个协程构建器都将 CoroutineScope 的实例添加到其代码块所在的作用域中。我们可以在这个作用域中启动 协程而无需显式 join 之,因为外部协程(示例中的 runBlocking)直到在其作用域中启动的所有协程都执 行完毕后才会结束。因此,可以将我们的示例简化为:

    image.png

    5.作用域构建器

    除了由不同的构建器提供协程作用域之外,还可以使用 coroutineScope 构建器声明自己的作用域。它会创建一 个协程作用域并且在所有已启动子协程执行完毕之前不会结束。

    runBlocking 与 coroutineScope 可能看起来很类似,因为它们都会等待其协程体以及所有子协程结束。主要 区别在于,runBlocking 方法会阻塞当前线程来等待,而 coroutineScope 只是挂起,会释放底层线程用于其他 用途。由于存在这点差异,runBlocking 是常规函数,而 coroutineScope 是挂起函数。
    可以通过以下示例来演示:

    image.png

    运行结果:
    Task from coroutine scope // 自己创建的协程作用域只delay100ms,delay时间最短,所以先打印
    Task from runBlocking // runBlocking自带的协程作用域delay200ms,第二个打印
    Task from nested launch // 自己创建的额协程作用域中delay了500ms,第三个打印
    Coroutine scope is over // 自己创建的协程作用域(soroutinesScope方法)内部都执行完才会向下执行,所以在自己创建的协程作用域执行完后才打印

    6.提取函数重构

    我们来将 launch { ...... } 内部的代码块提取到独立的函数中。当你对这段代码执行“提取函数”重构时,你会 得到一个带有 suspend 修饰符的新函数。这是你的第一个挂起函数。在协程内部可以像普通函数一样使用挂 起函数,不过其额外特性是,同样可以使用其他挂起函数(如本例中的 delay)来挂起协程的执行。

    image.png

    运行结果:
    Hello,
    World!

    7.协程很轻量

    image.png

    它启动了 10 万个协程,并且在 5 秒钟后,每个协程都输出一个点。
    现在,尝试使用线程来实现。会发生什么?(很可能你的代码会产生某种内存不足的错误)
    启动10 万个协程并不会创建10 万个线程

    8.全局协程像守护线程

    以下代码在 GlobalScope 中启动了一个⻓期运行的协程,该协程每秒输出“I'm sleeping”两次,之后在主函数中 延迟一段时间后返回。

    image.png

    你可以运行这个程序并看到它输出了以下三行后终止:

    image.png

    在 GlobalScope 中启动的活动协程并不会使进程保活。它们像守护线程一样。

    相关文章

      网友评论

          本文标题:Kotlin进阶-协程基础

          本文链接:https://www.haomeiwen.com/subject/lckzzltx.html