前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Kotlin协程开篇

Kotlin协程开篇

作者头像
PhoenixZheng
发布于 2021-04-09 02:29:48
发布于 2021-04-09 02:29:48
95100
代码可运行
举报
运行总次数:0
代码可运行

《Kotlin协程》均基于Kotlinx-coroutines 1.3.70

新开个坑,专门讲kotlin的协程。聊协程之前先说一下具体聊的是协程的什么内容。

· 协程是什么?

· 什么时候用协程?

· 协程的核心是什么?

· kotlin的协程和其他语言的协程有什么异同?

kotlin的协程的出现其实比kotlin语言还晚一点。在当前这个版本,协程甚至都还处于一个不稳定的迭代版本中。协程到目前为止都还没进入kotlin的标准库,它是一个独立的依赖库,叫 Kotlinx。对于想在开发中使用协程的人来说,需要在依赖里加入kotlinx-core依赖。作为一个独立的依赖包,它的源码可以从github上获取,《Kotlin协程》分析的源码就是以github上的master分支为参考。

协程没那么难

协程的出现是为了解决异步编程中遇到的各种问题。从高级编程语言出现的第一天,异步执行的问题就伴随出现。

在Kotlin里使用协程非常方便,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}

上面的代码是一个常规启动协程的方式,关键函数只有 launch,delay,这两个函数是kotlin协程独有的。其他函数都属于基本库。

代码的输出结果是

Hello, World!

这是一个典型的异步执行结果,先得到 Hello,而不是按代码顺序先得到 World。

异步执行在平时开发中经常遇到,比如执行一段IO操作,不管是文件读写,还是网络请求,都属于IO。

Android中我们对IO操作的一个熟知的规则是不能写在主线程中,因为它会卡线程,导致ANR。而上面的代码其实是不会卡线程的。用同步的方式写异步代码 这句话在很多资料中出现过,划重点。

理解这句话的关键在于,协程干了什么,让这个异步操作不会卡主线程?

我们知道类似的技术在RxJava中也有,它通过手动切线程的方式指定代码运行所在的线程,从而达到不卡主线程的目的。而协程的高明和简洁之处在于,开发者不需要主动切线程。

在上面的代码中打印一下线程名观察结果。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        println("Thread: ${Thread.currentThread().name}")
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Thread: ${Thread.currentThread().name}")
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}

我们会得到

Thread: main Hello, Thread: DefaultDispatcher-worker-1 World!

可以看到在打印World的时候,代码是运行在子线程的。

协程其实没那么容易

对于经常用协程开发的人来说,有几个很有意思的问题值得思考下。· 上面代码中的Thread.sleep()可以改成delay()吗?

· 为什么理论上可以开无限多个coroutine?

· 假设有一个IO操作 foo() 耗时a,一个计算密集操作 bar() 耗时b,用协程来执行的话,launc{a b} 耗时c,c是否等于a + b?

另外一个很有意思的问题需要用代码来展示。在协程中有一个函数 runBlocking{},没接触过的可以简单理解为它等价于launch{}。

用它来改造上面的代码,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking


fun main() = runBlocking {
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        println("Thread: ${Thread.currentThread().name}")
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Thread: ${Thread.currentThread().name}")
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}

我们会得到

Thread: DefaultDispatcher-worker-1 Thread: main Hello, World!

现在我们把 GlobalScope.launch这行改造一下,

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main() = runBlocking {
    launch { // 在后台启动一个新的协程并继续
        println("Thread: ${Thread.currentThread().name}")
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Thread: ${Thread.currentThread().name}")
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}

现在再看执行结果,

Thread: main Hello, Thread: main World!

WTF? launch里的代码也执行在主线程了?

这个问题涉及到Kotlin协程的Scope,调度,也是协程的实现核心逻辑

Kotlin不是第一个提出协程的

实际上在Kotlin之前就有不少语言实践了协程这个概念。比如python,golang。

而最原始的协程其实不叫协程,叫纤程(Fiber)。听说过Fiber的人都已经。。

甲:听说过纤程吗 乙:Fiber是吧 甲:你今年起码40岁了吧

纤程是微软第一个提出的,但因为它的使用非常的反人类,对程序员的代码质量要求非常高,以至于没人愿意用它。虽然现在还可以在微软官网上找到关于纤程的资料,但能用好纤程的程序员凤毛麟角。

Using Fibers

直到golang的出现,才把协程这个技术发扬光大。有人说python也有协程呀,为什么是golang。其实python的协程不是真正意义上的协程,后面我们会说到。python的协程是基于yield关键字进行二次封装的,虽然在高层抽象上也是以函数作为协程粒度,但对比golang差的太远。

golang做了什么

golang的协程叫goroutine,跟kotlin的coroutine差不多。golang用一种程序员更容易理解的抽象定义了协程粒度goroutine,还有它的各种操作。

对于程序员来说,再也不用关心什么时候切协程,协程在什么线程运行这种问题,开发效率和代码运行效率得到成倍提升。

golang在编译器上做了很多优化,当代码中发生IO或者内核中断的时候,会自动帮你切协程。熟悉计算机原理的能明白,当发生内核中断的时候,比如请求一个磁盘文件,中断发生时CPU其实是没有工作的,执行逻辑在这个时候处于一个空转,直到中断返回结果才继续往下执行。

于是在中断发生的时候,CPU相当于浪费了一段时间。golang在这个时候切协程,就能把CPU浪费的算力利用起来交给另外一个协程去执行。

kotlin的协程还在发展

如果去看kotlin的协程源码的话会发现里面有很多 exeprimental 的api和实现逻辑。直到1.3.70为止,jetbain团队还在继续地为coroutine机制增加新的活力。目前来说coroutine处于一个稳定阶段,可以基于1.3.70版本来分析它,后面应该不会有很大机制上的变动了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Android每日一讲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Kotlin---协程的使用
在使用协程之前,需要保证Kotlin-Gradle-Plugin的版本高于1.3。目前最高的版本为1.3.11。否则编译会报错
None_Ling
2019/02/25
1.4K0
Coroutine(协程)(一)
Coroutine是kotlin官方文档上推荐的,个人理解,其实就是一个轻量级的线程库。当然,协程并不是线程.简单来说,线程(thread)的调度是由操作系统负责,线程的睡眠、等待、唤醒的时机是由操作系统控制,开发者无法决定。使用协程,开发者可以自行控制切换的时机,可以在一个函数执行到一半的时候中断执行,让出CPU,在需要的时候再回到中断点继续执行。因为切换的时机是由开发者来决定的,就可以结合业务的需求来实现一些高级的特性。
提莫队长
2021/03/04
8840
Kotlin语言基础入门到熟悉:Kotlin协程基础
delay是非阻塞的,Thread.sleep是阻塞的。显式使用 runBlocking 协程构建器来阻塞。
Android_anzi
2022/02/22
8350
Kotlin 并发编程之"协程"
Kotlin, as a language, provides only minimal low-level APIs in its standard library to enable various other libraries to utilize coroutines. Unlike many other languages with similar capabilities, async and await are not keywords in Kotlin and are not even part of its standard library. Moreover, Kotlin's concept of suspending function provides a safer and less error-prone abstraction for asynchronous operations than futures and promises.
一个会写诗的程序员
2019/07/14
9430
《Kotin 极简教程》第9章 轻量级线程:协程(1)
在常用的并发模型中,多进程、多线程、分布式是最普遍的,不过近些年来逐渐有一些语言以first-class或者library的形式提供对基于协程的并发模型的支持。其中比较典型的有Scheme、Lua、Python、Perl、Go等以first-class的方式提供对协程的支持。
一个会写诗的程序员
2018/08/17
1.2K0
Kotlin协程-协程派发和调度框架
一个coroutine创建好之后,就交给协程框架去调度了。这篇主要讲从launch{...}开始,到最终得到执行的时候,所涉及到的协程框架内部概念。
PhoenixZheng
2021/04/26
1.1K0
Kotlin中的协程及在Android中的应用
Kotlin的一个协程可以理解为是运行在线程上的一个执行任务并且该任务可以在不同的线程间切换,一个线程可以同时运行多个协程。
码客说
2024/03/29
3960
Kotlin协程-特殊的阻塞协程
阻塞协程是种特殊的协程启动方式,一般是用 runBlocking{} 扩起来一段协程。
PhoenixZheng
2021/05/17
2.5K0
Kotlin 协程之Practice
Kotlin 练习参考https://www.kotlincn.net/docs/reference/
Yif
2019/12/26
1.2K0
Kotlin 协程的上下文和调度器介绍-Dispatchers
协程的上下文通常是CoroutineContext类型为代表。这个类型是被定义在Kotlin的标准库中。
zinyan.com
2023/07/13
5090
Kotlin 协程的上下文和调度器介绍-Dispatchers
Kotlin | 协程使用手册(不间断更新)
在概念上,async 就类似于 launch。它启动了一个单独的协程,这是一个轻量级的线程并与其它所有的协程一起并发的工作。不同之处在于 launch 返回一个 Job 并且不附带任何结果值,而 async 返回一个 Deferred —— 一个轻量级的非阻塞 future, 这代表了一个将会在稍后提供结果的 promise。你可以使用 .await() 在一个延期的值上得到它的最终结果, 但是 Deferred 也是一个 Job,所以如果需要的话,你可以取消它。
Petterp
2022/02/09
2.5K0
Kotlin | 协程使用手册(不间断更新)
Kotlin 协程-暂停与取消
我们在进行开发的过程中。往往会由于各种需求会需要控制后台协程的细粒度。比如,界面关闭了。那么在这个界面中启动的协程已经不需要再执行了。
zinyan.com
2023/07/14
9410
Kotlin 协程-暂停与取消
kotlin--协程的启动和取消
launch:我们之前已经使用过了GlobalScope的launch来启动协程,它返回一个Job async:返回一个Deferred,它也是一个Job,但是可以使用await函数获得运行的结果 除了之前结构化并发中介绍的几种指定CoroutineScope的API外,我们还可以使用runBlocking函数来指定CoroutineScope,他会使用主线程来转换成协程 launch和async内如果有子协程,那么该协程会等待子协程执行结束
aruba
2021/12/06
1.1K0
kotlin--协程的启动和取消
android之GlobalScope(协程)使用介绍
协程(Coroutines)是一种比线程更加轻量级的存在,正如一个进程可以拥有多个线程一样,一个线程可以拥有多个协程。
李小白是一只喵
2021/01/21
2.6K0
Kotlin协程-一个协程的生命周期
在安卓或者kotlin平台上使用协程是很简单的一件事情。举一个最简单的例子,不依赖安卓平台的协程代码,
PhoenixZheng
2021/04/26
1K0
Kotlin协程-一个协程的生命周期
Coroutine(协程)和retrofit
Coroutine是kotlin官方文档上推荐的,个人理解,其实就是一个轻量级的线程库 使用前加依赖
提莫队长
2020/06/02
1.4K0
kotlin 协程入门教程
链接:https://juejin.cn/post/7370994785655767067
Rouse
2024/05/28
2480
kotlin 协程入门教程
6个Android Kotlin协程相关面试题
解答: runBlocking是一个协程构建器,它会立即启动协程并在当前线程阻塞,直到协程执行完成。这通常用于主函数或测试中,以同步方式执行异步代码。然而,runBlocking在Android中可能会导致主线程阻塞,从而影响UI的响应性,因此应谨慎使用。
AntDream
2024/11/19
4750
6个Android Kotlin协程相关面试题
Kotlin---使用协程的异步
协程与协程间不能直接通过变量来访问数据,会导致数据原子性的问题,所以协程提供了一套Channel机制来在协程间传递数据。
None_Ling
2019/02/25
2.9K0
kotlin--协程上下文、异常处理
当我们在a协程延迟函数100ms之前开启一个子协程b,b做了200ms的事情,如果不考虑调度消耗的时间,那么a协程的生命也会延长成200ms
aruba
2021/12/06
9770
kotlin--协程上下文、异常处理
推荐阅读
相关推荐
Kotlin---协程的使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验