IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> kotlin协程的简述和使用 -> 正文阅读

[移动开发]kotlin协程的简述和使用

为什么会有协程 && 什么是协程

当我们最初学习程序之时,我们书写代码、使用指令执行,完成逻辑链条的前后关系。代码执行到哪,逻辑就走到哪。但问题随之出现,有些过程并不是能立即得到结果的,监听按钮或者一些耗时操作如IO操作等,程序为了等待结果就会阻塞。在一些应用场景之下,我们常常会使用异步api,通过一些回调函数操作来完成异步任务。

但是异步回调也有本身的问题,第一是,原本的统一的同步逻辑被拆分成几个阶段,造成代码可读性不好。第二是,在复杂的应用场景下,可能造成十分难受的回调地狱。以及难以debug的问题。

// JavaScript展示地狱回调
setTimeout(function () {  // 第一层
    console.log('第一层'); // 等3秒打印,再执行下一个回调函数
    setTimeout(function () {  // 第二层
        console.log('第二层'); // 等2秒打印,再执行下一个回调函数
        setTimeout(function () {   // 第三层
            console.log('第三层'); // 等1秒打印
        }, 1000)
    }, 2000)
}, 3000)

协程则应运而生。协程就是协作式多任务,协程本身是一种任务调度机制,也可以说是一种编程设计思想并不限定语言(在1958年就被发明,并用于构建汇编程序)。协程可以使用同步的代码逻辑流去操作异步的控制流。并且不会导致在操作系统的线程阻塞。相对于线程之间的切换,协程是更轻量级的。对于线程的操作是调用了操作系统的功能,而启用协程则是编程语言来完成,所以协程也被称为用户态线程。

协程为并发而生,线程在CPU多核之下,已然能完成并行。如图所示,并发本身是对CPU时间片的争夺。而对于异步任务进行线程切换,这才是协程能做的事。下图为CPU时间片段对于串行、并行、并发的线程处理的区别。

协程对于异步任务是主动让出而不是抢占的多任务,突出是主动让出;而线程则是抢占式的多任务,突出被动抢占。

抢占是一个相对低效的操作,“打断”这种操作不是那么容易做的,操作系统级别上之所以需要抢占是为了避免任务占着CPU不走。但在你自己知根知底的代码还要去抢占,这基本上就是牺牲性能的操作。所以使用协程避免抢占可以提高性能。

部分语言对于协程的支持

  • go语言的协程,直接在语法层面支持协程。解决了服务端开发中,IO密集型任务,并发性能程序过于复杂的痛点。go可以很优雅地进行高并发场景的开发。go语言的协程叫 Goroutines,从英文拼写就知道它和 Coroutines 还是有些差别的(设计思想上是有关系的),不然Kotlin的协程完全可以叫 Koroutines。
  • 之前Java对于协程而言,可以使用NIO(new IO)和一些多线程api进行操作,能模拟出一定的协程的效果,但实际开发方面还是过于麻烦,也许go语言在服务器端异军突起,在协程并发方面Java落于下风不无关系。
  • 2022年9月份,Oracle正式发布了最新版本的Java19和对应的Java虚拟线程的特性。是为帮助提高大型服务器的应用性能。Java虚拟线程在设计思想上,就是轻量级线程。值得关注的是,这次改动对于Java各个Api的改动很少,并没有太多新语法。Java19的虚拟线程是预览特性,很可能在Java21才会成为正式特性。
  • 很多语言都有自己的协程(虚拟线程)的技术,除了Go、Java,还有C#、Erlang、Lua等等。

kotlin协程

协程概念本身与线程概念是同一级别的东西。

在kotlin-JVM、Android平台中,对于协程的运用本质成为了切换线程,在性能上本身不会比优化后的线程池强。这是kotlin-JVM语言对于协程的实现。甚至在其他平台,kotlin-native、kotlin-javascript上,协程的实现的方法都完全不一样。在这个基础上,可以说,协程未必等于切线程,而未必就不能强于线程池。只是kotlin-JVM的协程实现就是基于切换线程。

kotlin协程写法

kotlin协程的写法和运用方案很多,有:runBlocking顶层函数;GlobalScope(CoroutineScope)单例对象调用launch开启协程;创建一个 CoroutineScope 对象开启协程等等。

这里我主要讲比较简单的开启协程的方法。

CoroutineScope(Dispatchers.Main).launch {
    // your code
    
    withContext(Dispatchers.IO) {
        // your blocking code
    }

    // your code

    withContext(Dispatchers.IO) {
        // your blocking code
    }
    
    // your code
    
}

如上的写法就是在main线程中进行异步操作,将阻塞和耗时操作放入withContext的切换线程的代码块里,kotlin程序就自然帮我们完成了线程之间的切换,以同步的写法完成异步的事情,这就是协程。

如下的代码是实现一个计时器更新主线程button的文字的功能,是kotlin使用协程对比使用线程池,kotlin-JVM协程在写法和性能上不一定更优秀,这些都是设计思想和方案选择的碰撞和抉择。如下代码,进行了简单的时间增加并在Android的UI线程更新的逻辑。

private fun useCoroutines() {
    CoroutineScope(Dispatchers.Main).launch {
        buttonCount.isEnabled = false
        var count = 0
        while (count < 10) {
            withContext(Dispatchers.IO) {
                delay(SECOND_DURATION)
                count++
            }
            buttonCount.text = String.format(
                Locale.getDefault(),
                "%d",
                count
            )
        }
        buttonCount.isEnabled = true
    }
}
private fun useExecutor() {
    Executors.newSingleThreadExecutor().execute {
        var count = 0
        while (count < 10) {
            SystemClock.sleep(SECOND_DURATION)
            count++
            val finalCount = count
            runOnUiThread {
                buttonCount.text = String.format(
                    Locale.getDefault(),
                    "%d",
                    finalCount
                )
            }
        }
        runOnUiThread { buttonCount.isEnabled = true }
    }
}

suspend关键字

suspend 是 Kotlin 协程最核心的关键字之一。官方解释是,代码执行到 suspend 函数的时候会『挂起』,并且这个『挂起』是非阻塞式的,它不会阻塞你当前的线程。

什么是挂起函数

suspend关键字修饰的函数叫做挂起函数,挂起函数只能在协程体内或者其他挂起函数内使用。协程内部挂起函数的调用处被称为挂起点,也就是Android Studio代码左边会出现的一个箭头加一个绿色波浪线的标志。

网上寻到的gif例子,具体挂起函数演示如下:

getUserInfo()、getFriendList(user)、getFeedList(friendList)三个函数都是挂起函数。内部必然为 suspend修饰的挂起函数。例如getUserInfo函数可为下内容:

suspend fun getUserInfo(): String {
    withContext(Dispatchers.IO) { // 切换到IO线程
        delay(1000L) // 延迟1s
    }
    return "content"
}

挂起的对象是协程,挂起的本质“就是这个协程从正在执行它的线程上脱离”。注意,这里不是这个协程停下来,而是脱离,从当前线程脱离,比如上文的代码,就是从主线程脱离,去IO或工作线程上进行处理。紧接着在 suspend 函数执行完成之后,协程为我们做的最爽的事就来了:会自动帮我们把线程再切回来。如上文所示,我们代码本身在主线程允许,当协程切走的函数执行完毕,协程会帮助我们post一个Runnable,让我们剩下的代码和信息继续回到之前的线程去运行。

参考文献

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 01:04:20  更:2022-09-30 01:04:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 21:22:55-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码