Kotlin协程:打破线程结构的思想
前语
协程是Kotlin比照Java的最大优势,需求理解协程的设计理念和常识体系,树立协程思想模型。本文将介绍协程的概念、特性和原理,以及如安在Android开发中运用协程来简化并发编程和优化软件架构。
什么是协程
协程是一种相互协作的程序,能够在恣意当地挂起和康复,每次回来一个值,而不是像一般程序那样只能在末尾回来一次。协程能够看成是一种用户态的轻量级线程,它不依靠于操作系统的调度,而是由程序员操控它们的履行流程。
举个比如,假设咱们有一个一般的函数getFilteredBitmap()
,它从一个API获取一张图片,而且给它加上一个雪花滤镜。咱们想要在主线程中调用这个函数,而且显现处理后的图片。咱们能够这样写:
fun showSnowyBitmap() {
val snowyBitmap = getFilteredBitmap() // 堵塞主线程
showBitmap(snowyBitmap) // 显现图片
}
这样的代码有一个很大的问题:getFilteredBitmap()
是一个耗时的操作,它会堵塞主线程,导致运用无响应。为了处理这个问题,咱们通常会运用异步回调或许RxJava等方法来在后台线程中履行这个操作,而且在主线程中接收成果。可是这样的代码会变得复杂和难以保护。
假如咱们把getFilteredBitmap()
改成一个协程,咱们就能够用一种更简练和直观的方法来写异步代码。咱们能够这样写:
suspend fun showSnowyBitmap() = coroutineScope {
val snowyBitmap = async { getFilteredBitmap() } // 在后台线程中发动一个协程
showBitmap(snowyBitmap.await()) // 在主线程中等待成果并显现图片
}
这样的代码看起来就像是同步的代码,但实际上它对错堵塞的。async
是一个协程构建器,它会在后台线程中发动一个新的协程,而且回来一个Deferred
目标,表明异步核算的成果。await
是一个挂起函数,它会挂起当时协程,直到Deferred
目标完结,而且回来成果。挂起当时协程并不会堵塞主线程,而是让出履行权给其他协程或许使命。当Deferred
目标完结时,当时协程会主动康复履行,而且继续后边的代码。
Kotlin协程
Kotlin协程需求独自依靠协程库,协程结构是一个全体的结构。协程比线程更轻量级、更灵敏、更高效,能够在不同线程间切换。
协程库
要运用Kotlin协程,咱们需求添加以下依靠到咱们的app的build.gradle文件:
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
}
这个库包含了以下几个部分:
-
kotlinx-coroutines-core
: 包含了中心的API和数据结构,如suspend
、launch
、async
、await
、CoroutineScope
、Job
、Deferred
等。 -
kotlinx-coroutines-android
: 包含了Android渠道特有的API和数据结构,如Main
调度器、LifecycleScope
等。 -
kotlinx-coroutines-test
: 包含了测验协程的东西类,如TestCoroutineDispatcher
、runBlockingTest
等。
协程结构
Kotlin协程结构是一个全体的结构,它包含了以下几个层次:
- 语言层:Kotlin语言供给了
suspend
关键字,用于标记挂起函数。挂起函数是一种特别的函数,它能够在不堵塞线程的情况下暂停和康复履行。挂起函数只能在协程或许其他挂起函数中调用。 - 库层:协程库供给了各种API和数据结构,用于创立和办理协程。主要有以下几个概念:
- 协程构建器:用于发动新的协程,如
launch
、async
、runBlocking
等。不同的协程构建器有不同的特色和用处,咱们会在后边具体介绍。 - 协程作用域:用于界说协程的生命周期规模,如
coroutineScope
、supervisorScope
、GlobalScope
等。协程作用域能够嵌套运用,形成一个结构化的并发形式。咱们会在后边具体介绍。 - 协程上下文:用于存储协程的相关信息,如调度器、Job、反常处理器等。协程上下文能够经过协程构建器或许作用域来指定或许修正。咱们会在后边具体介绍。
- 调度器:用于指定协程运转的线程或许线程池,如
Dispatchers.Main
、Dispatchers.IO
、Dispatchers.Default
等。调度器是协程上下文的一部分,咱们会在后边具体介绍。 - Job:用于表明一个协程的使命状况,如是否激活、是否完结、是否撤销等。Job也是协程上下文的一部分,咱们会在后边具体介绍。
- Deferred:用于表明一个异步核算的成果,它是一种特别的Job,能够经过
await()
来获取成果。Deferred通常由async()
来创立,咱们会在后边具体介绍。
- 协程构建器:用于发动新的协程,如
- 渠道层:不同的渠道供给了不同的完结方法,如JVM、Android、JS、Native等。渠道层主要负责完结挂起函数的机制和调度器的逻辑。
协程和线程
协程和线程都是一种并发编程的方法,可是它们有很多不同点:
- 线程是操作系统等级的概念,它依靠于操作系统的调度和办理。线程之间的切换需求保存和康复寄存器和栈信息,耗费较大。线程之间的通讯需求运用同享内存或许音讯队列等方法,容易呈现竞态条件或许死锁等问题。
- 协程是用户态等级的概念,它依靠于程序员操控它们的履行流程。协程之间的切换只需求保存和康复少量信息,耗费较小。协程之间能够运用通道或许同享流等方法进行通讯,愈加安全和高效。
协程的思想模型
协程能够看成是运转在线程中的Task,每个Task都有一个”抓手”或许”挂钩”,能够便利咱们对它进行挂起和康复。协程不会和特定的线程绑定,它能够在不同的线程之间灵敏切换。
咱们能够用一个图来表明协程的思想模型:
+-----------------+ +-----------------+ +-----------------+
| Thread 1 | | Thread 2 | | Thread 3 |
+-----------------+ +-----------------+ +-----------------+
| | | | | |
| +---------+ | | +---------+ | | +---------+ |
| | Task A |--->|---->| | Task A |--->|---->| | Task A | |
| +---------+ | | +---------+ | | +---------+ |
| | | | | |
| +---------+ | | +---------+ | | +---------+ |
| | Task B |--->|---->| | Task B |--->|---->| | Task B |--->|
| +---------+ | | +---------+ | | +---------+ |
| | | | | |
| +---------+ | | +---------+ | | +---------+ |
| | Task C |--->|---->| | Task C |--->|---->| | Task C |--->|
| +---------+ | | +---------+ | | +---------+ |
| | | | | |
+-----------------+ +-----------------+ +-----------------+
在这个图中,咱们有三个线程和三个协程(Task A、B、C)。每个协程都能够在恣意的线程中运转,只需有一个”抓手”或许”挂钩”来操控它们的挂起和康复。这个”抓手”或许”挂钩”就是协程上下文,它包含了调度器、Job、反常处理器等信息。咱们能够经过协程构建器或许作用域来指定或许修正协程上下文。
协程的非堵塞特性
由于协程具有挂起和康复的才能,它就能够完结非堵塞的特性。这意味着当一个协程遇到耗时的操作时,它不会堵塞后边的使命履行,而是让出履行权给其他使命,比及适宜的时机再康复履行。
举个比如,假设咱们有两个协程,分别是Task A和Task B。Task A需求从网络获取一些数据,Task B需求从数据库获取一些数据。咱们能够这样写:
suspend fun getData() = coroutineScope {
val dataFromNetwork = async { fetchFromNetwork() } // 在后台线程中发动一个协程,从网络获取数据
val dataFromDatabase = async { fetchFromDatabase() } // 在后台线程中发动一个协程,从数据库获取数据
val result = combine(dataFromNetwork.await(), dataFromDatabase.await()) // 在主线程中等待成果并组合
result // 回来成果
}
在这个比如中,咱们运用了async
协程构建器来发动两个协程,分别是dataFromNetwork
和dataFromDatabase
。它们都回来一个Deferred
目标,表明异步核算的成果。咱们运用了await()
挂起函数来等待它们的成果,而且组合成一个终究的成果。
留意,当咱们调用await()
时,并不会堵塞当时线程,而是挂起当时协程。这样,其他协程或许使命就能够在当时线程中履行。当Deferred
目标完结时,当时协程会主动康复履行,而且继续后边的代码。
这样的代码有以下几个优点:
- 它对错堵塞的,不会影响主线程的响应性。
- 它是并发的,能够一起履行多个耗时的操作。
- 它是顺序的,能够按照咱们期望的顺序写代码,而不需求运用回调或许嵌套等方法。
- 它是结构化的,能够运用协程作用域来办理协程的生命周期和反常处理。
总结
本文介绍了Kotlin协程的概念、特性和原理,以及如安在Android开发中运用协程来简化并发编程和优化软件架构。咱们学习了以下几个常识点:
-
协程是一种相互协作的程序,能够在恣意当地挂起和康复,每次回来一个值。
-
Kotlin协程需求独自依靠协程库,协程结构是一个全体的结构。
-
协程比线程更轻量级、更灵敏、更高效,能够在不同线程间切换。
-
协程能够看成是运转在线程中的Task,每个Task都有一个”抓手”或许”挂钩”,能够便利咱们对它进行挂起和康复。
-
协程具有非堵塞的特性,能够让出履行权给其他使命,比及适宜的时机再康复履行。