【Coroutines】Full Understanding of Kotlinx.Corutines Framework

embedded/2024/12/26 23:53:41/

文章目录

          • What is Corutines
          • Difference between Corutine and Thread
          • Fast Usage
          • Suspend Function
          • Advanced Usage of Coroutine
          • Coroutine Essentials
          • CoroutineContext
          • CoroutineScope
          • Predefined CoroutineScope
          • Predefined Dispatchers
          • Predefined CoroutineStart
          • Job
          • Create a Coroutine
          • ContinuationInterceptor
          • CoroutineExceptionHandler

What is Corutines

corutines is a modern way of async programming

a corutine is a async code block that supports suspend and resume

typically the code block looks like a synchronous block, but actually executed in async way

Difference between Corutine and Thread

both corutine and thread can achieve async works

for this point, they are the same, corutine can be regard as light-weight thread

but in fact, corutine is more a programming pattern, it cares how to write code easily

while thread is a hardware concept, it cares how cpu really works in concurrent situation

multiple corutines can run on same thread, also can run on different threads

corutines emphase how to write async code, while threads emphase the actual performance

corutines depend on threads, but it simplify traditional async programming with thread apis

Fast Usage

launch start a corutine to handle async work

step 2 will output before step 1 , because coroutine will not block code in host

kotlin">import kotlinx.coroutines.*suspend fun main() {GlobalScope.launch {delay(1000L)println("step 1")}println("step 2")delay(999*1000L)
}

async start a corutine that have a return value

the return value which called deferred , can be used in other corutine

step 1 will output before step 2 this time, as corutine 2 will wait result of corutine 1

step 3 will still output before step 1

kotlin">import kotlinx.coroutines.*suspend fun main() {val deferred = GlobalScope.async {delay(5000L)println("step 1")return@async 100}GlobalScope.launch {println("step 2 ${deferred.await()}")}println("step 3")delay(99000L)
}
Suspend Function

from demos above, we can see a keyword called suspend

suspend means that this function can hang up, and continue handling work in another place

suspend function can only be called from coroutine block, or other suspend functions

because only coroutine has the ability to suspend and resume, normal functions was not able to do this

behavior contrary to suspend is resume , which is often hidden under the mask of coroutines framework

Advanced Usage of Coroutine

there are many variant forms to use coroutines

you can launch a coroutine using kotlinx.coroutines library like this

kotlin">import kotlinx.coroutines.*suspend fun main() {val parentJob = Job()val scope = CoroutineScope(parentJob)val dispatcher = Dispatchers.Defaultval start = CoroutineStart.LAZYval job = scope.launch(dispatcher, start) {println("coroutine by launch")}job.start()delay(999*1000L)
}
Coroutine Essentials

a coroutine contains may composed of many essentials

now, let’s introduce them one by one, please take patience here

  • CoroutineContext : all essentials that determine how coroutine works
  • CoroutineScope : offer a scope of current coroutine, determine which coroutine you are writting in
  • CoroutineDispatcher : decide which thread coroutine work on
  • CoroutineStart : decide when to execute coroutine block
  • CoroutineExceptionHandler : how to handle exception when error occurs
  • ContinuationInterceptor : intercept current coroutine, and create a new coroutine based on current
  • most common implementation of ContinuationInterceptor is CoroutineDispatcher
  • Job : present the task that coroutine handles, can be cancelled manually
  • CoroutineName : give a name for current coroutine
CoroutineContext

CoroutineContext is the base class of most coroutine essential instances

coroutine context can be a single essential, or a collection of multiple essentials

context can be a standalone essential, also can hold some child essentials

likes a data struct below

kotlin">class Context : Map<Key, Context>

a coroutine context can be added to another context, then form a new context contains both of them

if key is not contained in current context, add it, otherwise, replace it

kotlin">val newCoroutineContext = currentContext + contextItem

coroutine essentials can be obtained from context through specific key

kotlin">val job = context[Job]
val name = context[CoroutineName]
val dispather = context[CoroutineDispatcher]
val interceptor = context[ContinuationInterceptor]
val errorHandler = context[CoroutineExceptionHandler]

CoroutineContext has a direct child interface called CoroutineContext.Element

most classes are directly inherited from Element but not Context

they are actually the same thing

but Element emphas the class is intented to resolve specific requirement, not act as a collection

CoroutineScope

coroutine scope hold a coroutine context object named coroutineContext

coroutineContext holds all essentials for current coroutine

design intents of CoroutineScope can cover two aspects

  • hide internal functions of actual CoroutineImpl
  • offer a block scope object to control current coroutine

coroutine scope can be cancelled, when scope is cancelled, coroutine is cancelled

in fact, coroutine scope cancell coroutine by delegate object obtained from coroutineContext[Job]

Predefined CoroutineScope
  • GlobalScope

    an empty scope, cannot be cancelled, always available

  • LifecycleScope

    bind with android LifecycleOwner, when lifecycle is destroyed, scope will be cancelled

  • ViewModelScope

    bind with android ViewModel, when ViewModel is cancelled, scope will be cancelled

Predefined Dispatchers
  • Dispatchers.Default

    post to default thread pool, designed to handle computation work

  • Dispatchers.IO

    post to default thread pool, designed to handle computation work

  • Dispatchers.Main

    post to ui thread, designed to handle ui work, implementation depend on platform adapter

  • Dispatchers.Unconfined

    not change thread, execute immediately

there are some details we should concern

  • Dispatchers.Default and Dispatchers.IO share the same thread pool

    but each thread has a flag, to indicate whether receive cpu-intensive-work or memory-intensive-work

  • when Dispatchers.Unconfined is continuously used in child coroutines

    tasks will be post to event loop queue, maintained in current thread, to avoid StackOverflow error

Predefined CoroutineStart

CoroutineStart define the start strategy of coroutines

before introduce of those strategies, let’s understand differences between dispatch and execute first

execute means coroutine block is executed

dispatch means coroutine block is post to thread or task queue, but not executed yet

execute always happens behind dispatch

  • CoroutineStart.DEFAULT

    dispatched immediately, can be cancelled before dispatch

  • CoroutineStart.ATOMIC

    dispatched immediately, but cannot be cancelled until block suspended

  • CoroutineStart.LAZY

    not dispatched, until start api is actively called, such as start join await

  • CoroutineStart.UNDISPATCHED

    executed immediately in current calling-stack, not dispatched, until block’s first suspend

CoroutineStart.DEFAULT and CoroutineStart.LAZY are the most common ways

while CoroutineStart.ATOMIC and CoroutineStart.UNDISPATCHED are design for special scenarios

Job

represent the task coroutine handles, returned when coroutine is created

can be used to start or cancel a coroutine, and query coroutine state

  • start : start coroutine
  • cancel : cancel coroutine
  • join : join another coroutine and wait completed
  • parent : get parent job
  • isCancelled : query whether coroutine is cancelled
  • isCompleted : query whether coroutine is completed
  • invokeOnCompletion : set cancel or complete callback
Create a Coroutine

kotlinx.coroutines framework offers several ways to create coroutines

  • CoroutineScope.launch

    create a coroutine, without blocking current calling stack

  • CoroutineScope.async

    create a coroutine, with a FutureResult called Deferred returned, not blocking current calling stack

    value in Deferred can be got by await, blocking current calling stack until value returned

  • withContext(CoroutineContext, CoroutineScope.() -> R)

    create a coroutine, with specified context, blocking current calling stack until block finished and value returned

  • CoroutineDispatcher.invoke(CoroutineScope.() -> R)

    create a coroutine, with specified dispatcher, blocking current calling stack until block finished and value returned

    in fact, this is a inline function, actually calls withContext(dispatcher, block)

  • runBlocking(CoroutineContext, CoroutineScope.() -> T)

    create a coroutine, with specified context, blocking until block finished and value returned

    this function will not only block calling stack, but also block current thread

    it is designed to use suspend-style apis in non-suspend functions

    only suggest using it in main function or unit test functions

  • coroutineScope(CoroutineScope.() -> R)

    create a coroutine based on current coroutine context, equivalent to withContext(coroutineContext, block)

  • supervisorScope(CoroutineScope.() -> R)

    like coroutineScope, but child coroutine’s exception won’t be delivered to parent coroutine

all coroutines will start automatically by default, unless you specify other start strategy

ContinuationInterceptor

interceptor can intercept current coroutine, and extend extra operations based on origin coroutine

taking Dispatcher as an example, it handles the same work as the origin coroutine, with a thread switch operation extended

CoroutineExceptionHandler

there are two chances can trigger exception

  • error occurs, and exception is thrown
  • error saved in result, and get result is called

there are several handlers can handle exception

  • exception handler from parent coroutine
  • exception handler from current coroutine
  • exception handler from thread

how exception deliver between coroutine depends on many factors

it is a complex subject, we will talk about it in next blog


http://www.ppmy.cn/embedded/134297.html

相关文章

​【Java基础面试题2】

目录 前言 1.11 int和Integer有什么区别&#xff0c;二者在做运算时会得到什么结果&#xff1f; 1.12 说一说你对面向对象的理解 1.13 面向对象的三大特征是什么&#xff1f; 1.14 封装的目的是什么&#xff0c;为什么要有封装&#xff1f; 1.15 说一说你对多态的理解 1…

Spring Boot 开发常见问题及解决方案汇总

Spring Boot 是目前 Java 开发中最受欢迎的框架之一&#xff0c;它简化了 Spring 框架的配置和部署流程。然而&#xff0c;开发者在使用 Spring Boot 过程中&#xff0c;也会遇到一些常见问题。本文将分析这些问题的排行&#xff0c;并提供相应的解决方案&#xff0c;帮助开发者…

15 Docker容器存储架构:docker存储驱动简介

文章目录 一、Docker 存储驱动探索1.1 存储驱动1.2 存储驱动方式1.3 非持久化存储1.4 持久化存储一、Docker 存储驱动探索 1.1 存储驱动 Storage driver处理各镜像层及容器层的处理细节,实现了多层数据的堆叠,为用户提供了多层数据合并后的统一视图。 [superman@docker ~]$…

QT国际化,语言翻译

文章目录 1.lupdate更新翻译2.生成*.ts文件3.翻译4.lrelease发布翻译5.在程序中使用翻译文件6.运行 1.lupdate更新翻译 lupdate就是用于扫描pro文件中指定的代码或UI文件中被tr包装起来的文本。 lupdate的使用 lupdate的使用可以使用lupdate --help来查看。 粗略的说一下这个…

手机实时提取SIM卡打电话的信令声音-新的篇章(三、Android虚拟声卡探索)

手机实时提取SIM卡打电话的信令声音-新的篇章(三、Android虚拟声卡探索) 前言 前面的篇章中&#xff0c;我们从理论方向和实际市面上出现的音频线传输声音的方式&#xff0c;讨论绕开手机对SIM卡电话通话声音的封锁场景的可行性&#xff0c;并实际选购几款数字和模拟的USB转接…

Linux的第二次作业

作业1 1.建组 [rootbogon 桌面]# groupadd -g 2000 shengcan [rootbogon 桌面]# groupadd -g 2001 caiwu [rootbogon 桌面]# groupadd -g 2002 jishu 2.建立用户并赋予相应权限 [rootlocalhost 桌面]# useradd -u 2000 -g shengcan -G …

08 go语言(golang) - 数据类型:数组、切片

数据类型 Go语言提供了一组丰富的数据类型&#xff0c;涵盖了基本的数值类型、复合类型和特殊类型。 基本数据类型 布尔型&#xff1a; bool&#xff1a;表示真或假&#xff08;true 或 false&#xff09;。 数值型&#xff1a; 整型&#xff1a;包括有符号和无符号整数。 有…

Leetcode 排序链表

这段代码的算法思想是 归并排序&#xff0c;一种适合链表的排序方法。它通过递归地将链表拆分成两部分&#xff0c;分别排序&#xff0c;然后合并已排序的部分&#xff0c;从而达到整体排序的目的。以下是代码的中文解释&#xff1a; 算法步骤&#xff1a; 找到链表的中点&…