参考链接

示例来自bilibili Kotlin语言深入解析 张龙老师的视频

1 如何取消协程

import kotlinx.coroutines.*/*** 协程的取消*/fun main() = runBlocking {// 得到启用的协程myJobval myJob = GlobalScope.launch {repeat(200) { i ->println("hello $i")delay(500)}}delay(1100)println("hello world")/*** Cancels this job with an optional cancellation cause.* A cause can be used to specify an error message or to provide other details on the cancellation reason for debugging purposes.* See Job documentation for full explanation of cancellation machinery.*/myJob.cancel(CancellationException("test exception"))// 取消协程 取消协程是一个正常的操作 因此实际上CancellationException不会被打印/*** Suspends the coroutine until this job is complete.* 取消协程不是立马结束的 取消协程时 cancel和join经常成对出现*/myJob.join()
//    myJob.cancelAndJoin() // 上面两句等价于cancelAndJoinprintln("end")
}class HelloKotlin {
}

2 协程取消的注意点

如果协程正处于某个计算过程中且没有检查取消状态,那么它就是无法被取消的

import kotlinx.coroutines.*/*** 协程取消的注意点* 如果协程正处于某个计算过程中且没有检查取消状态,那么它就是无法被取消的** kotlinx.coroutines包下所有的挂起函数都可以被取消* 当调用取消方法时 他们会检查取消状态,当取消时抛出CancellationException异常* 不过 如果协程正处于某个计算过程中且没有检查取消状态,那么它就是无法被取消的(可以理解为取消失败)*/fun main() = runBlocking {val startTime = System.currentTimeMillis()val job = launch(Dispatchers.Default) {// 取得启动的协程jobvar nextTime = startTimevar i = 0;while (i < 20) {// 在时间没有到达指定时间 该while循环一直在空轮转(while循环满足 而 if条件不满足)if (System.currentTimeMillis() > nextTime) {// 每500毫秒 i++ 直到i>=20结束println("job:I am sleeping ${i++}")nextTime += 500}}println("end while")}delay(1300)println("hello world")job.cancelAndJoin() // 取消协程 但是协程处于计算中 且协程没有检查取消状态 因此协程没有被取消 一直等待 直到协程执行完毕println("welcome")
}/**
输出如下
job:I am sleeping 0
job:I am sleeping 1
job:I am sleeping 2
hello world
welcome
job:I am sleeping 3
job:I am sleeping 4
job:I am sleeping 5
job:I am sleeping 6
job:I am sleeping 7
job:I am sleeping 8
job:I am sleeping 9
job:I am sleeping 10
job:I am sleeping 11
job:I am sleeping 12
job:I am sleeping 13
job:I am sleeping 14
job:I am sleeping 15
job:I am sleeping 16
job:I am sleeping 17
job:I am sleeping 18
job:I am sleeping 19
end while*/class HelloKotlin2 {
}

3 协程取消的注意点

如果协程正处于某个计算过程中 如何正确取消协程

import kotlinx.coroutines.*/*** 协程取消的注意点* 如果协程正处于某个计算过程中 如何正确取消协程** 有两种方式可以让协程能够被成功取消* 1 周期性调用一个挂起函数 该挂起函数会检查取消状态 例如挂起函数delay* 2 显示地检查取消状态** 下面地示例采用第二种方式取消协程*/fun main() = runBlocking {val startTime = System.currentTimeMillis()val job = launch(Dispatchers.Default) {// 取得启动的协程jobvar nextTime = startTimevar i = 0;/***   Returns true when the current Job is still active (has not completed and was not cancelled yet).*   Check this property in long-running computation loops to support cancellation:*   while (isActive) {*   // do some computation*   }* isActive是CoroutineScope地一个扩展属性* 它当协程仍然处于活跃状态(还没有完成且没有取消)返回true* 在长期计算循环中检查该属性可以让协程能够被取消*/while (isActive) {// 与HelloKotlin2唯一不同地方if (System.currentTimeMillis() > nextTime) {// 每500毫秒 i++ 直到i>=20结束println("job:I am sleeping ${i++}")nextTime += 500}}println("end while")}delay(1300)println("hello world")job.cancelAndJoin() // 取消协程 但是协程处于计算中 但是它检查取消状态 因此协程立即被取消println("welcome")
}/**
输出:
job:I am sleeping 0
job:I am sleeping 1
job:I am sleeping 2
hello world
end while
welcome*/class HelloKotlin3 {
}

4 协程取消的注意点

如果协程正处于某个计算过程中 如何正确取消协程2

import kotlinx.coroutines.*/*** 协程取消的注意点* 如果协程正处于某个计算过程中 如何正确取消协程2** 有两种方式可以让协程能够被成功取消* 1 周期性调用一个挂起函数 该挂起函数会检查取消状态 例如挂起函数delay* 2 显示地检查取消状态** 下面地示例采用第二种方式取消协程* 这里我们实际上用的是HelloKotlin1的代码** 对比HelloKotlin3 HelloKotlin3_1之所以可以成功取消协程 因为* 1.cancel中修改状态* 追踪cancel的实现* 以JobSupport为例 按照顺序追踪 cancel cancelInternal cancelImpl makeCancelling 内部最终会修改协程isActive的状态* 2.delay这个挂起函数检查state* delay:* Delays coroutine for a given time without blocking a thread and resumes it after a specified time.* This suspending function is cancellable.* If the Job of the current coroutine is cancelled or completed while this suspending function is waiting,* this function immediately resumes with CancellationException.* Note that delay can be used in select invocation with onTimeout clause.* Implementation note: how exactly time is tracked is an implementation detail of CoroutineDispatcher in the context.* Params:* timeMillis - time in milliseconds.* 延迟指定时间的协程而不会阻塞线程 并且在等待时间到达的时候 协程能够再次启动* 该挂起方法是可以取消的* 如果当前协程的job在挂起方法正在等待时被取消或完成* 那么该方法会被立马继续并抛出CancellationException** 总结一下cancel将isActive更改为false 并且挂起函数可以检测到状态变化/或者可以主动判断isActive的值* 说到底 协程要能取消成功 需要感知到isActive状态的变化*/fun main() = runBlocking {// 得到启用的协程myJobval myJob = GlobalScope.launch {repeat(200) { i ->println("hello $i")delay(500)}}delay(1100)println("hello world")/*** Cancels this job with an optional cancellation cause.* A cause can be used to specify an error message or to provide other details on the cancellation reason for debugging purposes.* See Job documentation for full explanation of cancellation machinery.*/myJob.cancel(CancellationException("test exception"))// 取消协程 取消协程是一个正常的操作 因此实际上CancellationException不会被打印/*** Suspends the coroutine until this job is complete.* 取消协程不是立马结束的 取消协程时 cancel和join经常成对出现*/myJob.join()
//    myJob.cancelAndJoin() // 上面两句等价于cancelAndJoinprintln("end")
}class HelloKotlin3_1 {
}

5 协程与finally执行顺序

/*** 使用finally来关闭资源* join 与 cancelAndJoin都会等待所有的清理动作完成之后才会继续往下执行*/fun main() = runBlocking {val myJob = launch {try {repeat(100) { i ->println("job sleeping $i")delay(500)}} finally {println("finally执行") // cancelAndJoin会等待finally执行完毕}}delay(1300)println("hello world")myJob.cancelAndJoin()println("end")
}class HelloKotlin4 {
}

6 如果已经取消的协程 再调用挂起方法(如delay)不能成功调用

/*** 如果已经取消的协程 再调用挂起方法(如delay)不能成功调用** 对于下面的示例 当我们在协程的finally中调用挂起函数时 会导致出现CancellationException异常 原因在于运行着该* 代码块的协程已经被取消了。通常情况下,这没有什么问题,因为大多数关闭动作(如取消一个job 关闭连接)都是瞬时的(非阻塞的)* 并不需要使用挂起函数;然而,在极少数情况下,我们想要在取消的协程中进行挂起操作时,我们可以将代码放置到withContext(NonCancellable){}* 代码块中,在这种结构中 我们实际使用了withContext函数与NoCancellable的上下文 我们在下一节演示withContext** 以下的示例中在主线程中延时1300毫秒* 同时协程在不断的等待输出* 当主线程延时结束 取消协程 finally中的delay执行失败 之后的语句不再执行*/
fun main() = runBlocking {val myJob = launch {try {repeat(100) { i ->println("job: I am sleeping $i")delay(500)}} finally {println("execute finally")delay(1000)// 因为走到这里时协程已经取消 会抛出CancellationException异常 因此后面的代码都不执行了println("finally code after delay")}}delay(1300)println("hello world")myJob.cancelAndJoin()println("end")
}class HelloKotlin5 {
}

7 已经取消的协程 再调用挂起方法(如delay)需要使用withContext代码块

/*** 已经取消的协程 再调用挂起方法(如delay)需要使用withContext代码块** 当我们在协程的finally中调用挂起函数时 会导致出现CancellationException异常 原因在于运行着该* 代码块的协程已经被取消了。我们想要在取消的协程中进行挂起操作时,我们可以将代码放置到withContext(NonCancellable){}* 代码块中,在这种结构中 我们实际使用了withContext函数与NoCancellable的上下文*/
fun main() = runBlocking {val myJob = launch {try {repeat(100) { i ->println("job: I am sleeping $i")delay(500)}} finally {///*** withContext: Calls the specified suspending block with a given coroutine context, suspends until it completes, and returns the result** NonCancellable* A non-cancelable job that is always active. It is designed for withContext function to prevent cancellation of code blocks that need to be executed without cancellation.* 防止代码块中的代码被取消* Use it like this:* withContext(NonCancellable) {* // this code will not be cancelled* }*/withContext(NonCancellable) {// 注意与上一个demo对比差异println("execute finally")delay(1000)println("finally code after delay")}}}delay(1300)println("hello world")myJob.cancelAndJoin()println("end")
}class HelloKotlin5_1 {
}

8 协程的超时

/*** 协程的超时 设置withTimeout* 我们在使用协程时,如果取消了协程,那么很大一部分原因在于协程的执行时间超过了指定时间:我们可以通过手工引用与协程对应的Job的* 方式来启动一个单独的协程用于取消这个协程,不过Kotlin提供了一个内建的函数帮我们更方便的实现这一点(withTimeout)*/fun main() = runBlocking {try {withTimeout(1900) {repeat(5) {// 修改为4 就不会抛出TimeoutCancellationExceptioni ->println("hello $i")delay(400)}}} catch (ex: TimeoutCancellationException) {}withTimeout(1900){repeat(5){// 修改为4 就不会抛出TimeoutCancellationExceptioni ->println("hello $i")delay(400)}}
}class HelloKotlin6 {}

9 协程的超时2

/*** 协程的超时设置withTimeoutOrNull* 由withTimeout函数所抛出的TimeoutCancellationException是CancellationException的子类,当CancellationException异常* 抛出时,我们并未在控制台上看到异常信息,因为在取消的协程中,CancellationException被认为是一种协程完成的正常原因* 既然CancellationException仅仅是一个异常而已 所有的资源都会以通常的方式来关闭,那么我们可以将相关的代码放到一个try...catch块中;* try {*     withTimeout(xxx) {*        ...*     }* }* catch (ex: TimeoutCancellationException) {* }** 此外,相对于withTimeout Kotlin还提供了一个更便利的方法withTimeoutNotNull* 从功能角度上看 他非常类似withTimeout,不过当超时发生时,它不会抛出CancellationException异常 而是直接返回null**/
fun main() = runBlocking {val res = withTimeoutOrNull(1900){repeat(4){// 修改为4 就不会抛出TimeoutCancellationExceptioni ->println("hello $i")delay(400)}"haha" // 协程没有超时 则res会被赋值为“haha” 否则 res等于null}println("result is $res")
}class HelloKotlin7 {
}

Kotlin学习笔记24 协程part4 协程的取消与超时相关推荐

  1. Kotlin 学习笔记(四)—— 协程的基础知识,面试官的最爱了~

    又是一个月没见了,坚持永远是世上最难的事情,但,往往难事才会有更大的收获.与君共勉~ 前段时间一直在学习 Compose,所以导致 Kotlin 笔记系列搁置了好久.一方面是因为 Compose 的学 ...

  2. Kotlin 学习笔记(十四)浅读协程

    上一篇-Kotlin 学习笔记(十三)高阶函数 为什么需要协程   举例一个异步编程中最常见的场景:后台线程执行一个A任务,下一个B任务依赖于A任务的执行结果,所以必须等待上一个任务执行完成后才能开始 ...

  3. Kotlin学习笔记(1)- 环境配置

    系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...

  4. Kotlin学习笔记20 阶段复习2

    参考链接 示例来自bilibili Kotlin语言深入解析 张龙老师的视频 lambda表达式 内联函数 Kotlin学习笔记 第三章 函数 高阶函数 lambda表达式 内联函数_积跬步 至千里- ...

  5. Kotlin 学习笔记(八)—— Kotlin类与对象之接口

    Kotlin 学习笔记(八)-- Kotlin类与对象之接口 Kotlin学习笔记系列教程 Kotlin 学习笔记(一)-- 概述.学习曲线.开发工具.参考资料 Kotlin 学习笔记(二)-- 基础 ...

  6. Kotlin学习笔记(3)- 语法

    系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...

  7. Kotlin学习笔记(2)- 空安全

    系列文章全部为本人的学习笔记,若有任何不妥之处,随时欢迎拍砖指正.如果你觉得我的文章对你有用,欢迎关注我,我们一起学习进步!kotlin学习笔记系列首发简书和CSDN Kotlin学习笔记(1)- 环 ...

  8. kotlin学习笔记——枚举、封闭类

    1.枚举 kotlin中提供类枚举的实现,与java相同.枚举可以带参数,如 enum Icon(val res: Int){MENU(R.drawable.menu),BACK(R.drawable ...

  9. kotlin学习笔记——重载操作符

    Kotlin中有很多操作符可以使用,具体可以见kotlin学习笔记--操作符_There is a Bug!!!-CSDN博客 Kotlin的一个特点就是可以重载这些操作符,为操作符赋予不同的行为. ...

最新文章

  1. DEDECMS v5.5 GBK Final 的一个鸡肋漏洞
  2. c#大圣之路笔记——c# SqlDataReader和SqlDataAdapter区别
  3. 操作系统(概述、组成)、用户态、内核态
  4. 内存溢出与内存泄漏区别
  5. 中科曙光服务器怎么装系统_如何给服务器装系统
  6. 重理工c语言实验指导书,太原理工大学级c语言实验指导书参考答案教程方案.docx...
  7. [Linux] rpm 与 dpkg 用法比较
  8. hbase hfile java_通过生成HFile导入HBase
  9. vue 动态拼接style_vue style width a href动态拼接问题的解决
  10. github网页链接
  11. Android检测版本更新
  12. duilib消息类型
  13. C语言 常用标准库函数 初学者常用
  14. 最新云优YUNUCMS企业网站管理系统
  15. 如何将手机哔哩哔哩缓存的m4s格式的视频转换成mp4呢?
  16. amd显卡风扇调节_AMD Crimson驱动程序导致显卡风扇速度过低,官方: 请尽快修复...
  17. 核磁共振基本原理——核磁共振现象
  18. 微信机器人控制linux,关于微信公共平台聊天机器人控制开启和关闭的代码
  19. 最新款带采集功能壁纸/头像/动态壁纸小程序上线超炫裂变超强支持投稿
  20. 红米无线连接服务器,电脑无线连接红米手机远程管理启动技巧

热门文章

  1. 计算机专业简历中技能特长怎么写,简历上特长技能应该怎么填写
  2. 平板android优化网络,Android版Firefox更新 针对平板设备优化
  3. 数据结构--hashtable(散列表)
  4. win10下安装lxml
  5. 使用mysql命令修改配置信息
  6. nginx 499 502 413 404 处理
  7. linux 线程池编程,Linux-C-9-线程池编程
  8. 判断sem信号量为零_kernel.sem信号量调优
  9. 基础编程题目集 7-4 BCD解密 (10 分)
  10. CCF201703-1 分蛋糕