Kotlin->Kotlin协程的取消机制

取消整个协程作用域CoroutineScope

kotlin 复制代码
Log.i("yang", "Main Start")
val mScope = CoroutineScope(Dispatchers.IO)

val mJob1 = mScope.launch {
    try {
        Log.i("yang", "Job1 Start")
        delay(1000)
        Log.i("yang", "Job1 End")
    } catch (e: Exception) {
        Log.i("yang", "Job1 Cancelled e = ${e.message}")
    }
}

val mJob2 = mScope.launch {
    try {
        Log.i("yang", "Job2 Start")
        delay(2000)
        Log.i("yang", "Job2 End")
    } catch (e: Exception) {
        Log.i("yang", "Job2 Cancelled e = ${e.message}")
    }
}

Log.e("yang", "Main Continue")
Thread.sleep(500)

mScope.cancel()
Log.e("yang", "Main Cancel")
Log.i("yang", "Main End")

// log
2025-01-06 17:36:52.588  1752-1752  yang                    com.example.myapplication            I  Main Start
2025-01-06 17:36:52.593  1752-1752  yang                    com.example.myapplication            E  Main Continue
2025-01-06 17:36:52.593  1752-2367  yang                    com.example.myapplication            I  Job1 Start
2025-01-06 17:36:52.594  1752-2391  yang                    com.example.myapplication            I  Job2 Start
2025-01-06 17:36:53.094  1752-1752  yang                    com.example.myapplication            E  Main Cancel
2025-01-06 17:36:53.094  1752-1752  yang                    com.example.myapplication            I  Main End
2025-01-06 17:36:53.094  1752-2367  yang                    com.example.myapplication            I  Job1 Cancelled e = Job was cancelled
2025-01-06 17:36:53.094  1752-2407  yang                    com.example.myapplication            I  Job2 Cancelled e = Job was cancelled
  • IO线程创建一个协程作用域CoroutineScope(Dispatchers.IO)依次启动两个协程任务,主线程在500ms后取消整个协程作用域,其中的两个协程任务都会取消
  • 主线程在取消协程作用域mScope.cancel()之后,其中两个JobStart语句会执行,End语句不会执行,因为取消协程作用域时,两个Job都抛出了异常

取消没有返回值的协程任务Job

withContext挂起函数检测取消信号

kotlin 复制代码
Log.i("yang", "Main Start")
val mJob = CoroutineScope(Dispatchers.IO).launch {
    try {
        Log.i("yang", "IO Start")
        Thread.sleep(1000) // 模拟耗时任务
        Log.i("yang", "IO End")
        withContext(Dispatchers.Main) {
            Log.i("yang", "Main withContext Start")
        }
    } catch (e: Exception) {
        Log.i("yang", "Launch Exception = ${e.message}")
    }
}
Log.i("yang", "Main Continue")
Thread.sleep(500)
mJob.cancel()
Log.i("yang", "Main End")

// log
2025-01-06 16:13:46.784 29337-29337 yang                    com.example.myapplication            I  Main Start
2025-01-06 16:13:46.785 29337-29337 yang                    com.example.myapplication            I  Main Continue
2025-01-06 16:13:46.785 29337-29622 yang                    com.example.myapplication            I  IO Start
2025-01-06 16:13:47.285 29337-29337 yang                    com.example.myapplication            I  Main End
2025-01-06 16:13:47.785 29337-29622 yang                    com.example.myapplication            I  IO End
2025-01-06 16:13:47.785 29337-29622 yang                    com.example.myapplication            I  Launch Exception = StandaloneCoroutine was cancelled
  • IO线程创建一个协程作用域CoroutineScope(Dispatchers.IO)并调用launch方法返回一个Job对象
    主线程在500ms后取消这个协程,但是协程体并不能立即感知自己被取消了,只会执行到withContext才会检测取消信号
  • IO线程上的所有log都会执行,withContext挂起函数之后的代码不再执行

delay挂起函数检测取消信号

kotlin 复制代码
Log.i("yang", "Main Start")
val mJob = CoroutineScope(Dispatchers.IO).launch {
    try {
        Log.i("yang", "IO Start")
        delay(1000)
        Log.i("yang", "IO End")
    } catch (e: Exception) {
        Log.i("yang", "Launch Exception = ${e.message}")
    }
}
Log.i("yang", "Main Continue")
Thread.sleep(500)
mJob.cancel()
Log.i("yang", "Main End")

// log
2025-01-06 16:35:38.555 29643-29643 yang                    com.example.myapplication            I  Main Start
2025-01-06 16:35:38.558 29643-29643 yang                    com.example.myapplication            I  Main Continue
2025-01-06 16:35:38.559 29643-30198 yang                    com.example.myapplication            I  IO Start
2025-01-06 16:35:39.059 29643-29643 yang                    com.example.myapplication            I  Main End
2025-01-06 16:35:39.060 29643-30200 yang                    com.example.myapplication            I  Launch Exception = StandaloneCoroutine was cancelled
  • IO线程创建一个协程作用域CoroutineScope(Dispatchers.IO)并调用launch方法返回一个Job对象
    主线程在500ms后取消这个协程,但是协程体并不能立即感知自己被取消了,只会执行到delay才会检测取消信号
  • IO线程上只有IO Start这一句Log会执行,delay挂起函数后的代码不再执行

CoroutineScope.isActive变量检测取消信号

kotlin 复制代码
Log.i("yang", "Main Start")
 var mJob = CoroutineScope(Dispatchers.IO).launch {
     try {
         Log.i("yang", "IO Start")
         Thread.sleep(1000)
         if (!isActive){
             Log.i("yang", "IO is not active")
             return@launch
         }
         Log.i("yang", "IO End")
     } catch (e: Exception) {
         Log.i("yang", "Launch Exception = ${e.message}")
     }
 }
 Log.i("yang", "Main Continue")
 Thread.sleep(500)
 mJob.cancel()
 Log.i("yang", "Main End")
Log.i("yang", "Main Continue")
Thread.sleep(500)
mJob.cancel()
Log.i("yang", "Main End")

// log
2025-01-06 16:48:17.754 30216-30216 yang                    com.example.myapplication            I  Main Start
2025-01-06 16:48:17.757 30216-30216 yang                    com.example.myapplication            I  Main Continue
2025-01-06 16:48:17.757 30216-30670 yang                    com.example.myapplication            I  IO Start
2025-01-06 16:48:18.257 30216-30216 yang                    com.example.myapplication            I  Main End
2025-01-06 16:48:18.758 30216-30670 yang                    com.example.myapplication            I  IO is not active
  • IO线程创建一个协程作用域CoroutineScope(Dispatchers.IO)并调用launch方法返回一个Job对象
    主线程在500ms后取消这个协程,但是协程体并不能立即感知自己被取消了,只会执行到!isActive才会检测取消信号,提前return
  • IO线程上只有IO Start这一句Log会执行,!isActive后的代码不再执行

CoroutineScope.ensureActive()函数检测取消信号

kotlin 复制代码
Log.i("yang", "Main Start")
var mJob = CoroutineScope(Dispatchers.IO).launch {
    try {
        Log.i("yang", "IO Start")
        Thread.sleep(1000)
        ensureActive()
        Log.i("yang", "IO End")
    } catch (e: Exception) {
        Log.i("yang", "Launch Exception = ${e.message}")
    }
}
Log.i("yang", "Main Continue")
Thread.sleep(500)
mJob.cancel()
Log.i("yang", "Main End")

// log
2025-01-06 17:17:36.781 31763-31763 yang                    com.example.myapplication            I  Main Start
2025-01-06 17:17:36.784 31763-31763 yang                    com.example.myapplication            I  Main Continue
2025-01-06 17:17:36.785 31763-32227 yang                    com.example.myapplication            I  IO Start
2025-01-06 17:17:37.285 31763-31763 yang                    com.example.myapplication            I  Main End
2025-01-06 17:17:37.785 31763-32227 yang                    com.example.myapplication            I  Launch Exception = StandaloneCoroutine was cancelled
  • IO线程创建一个协程作用域CoroutineScope(Dispatchers.IO)并调用launch方法返回一个Job对象
    主线程在500ms后取消这个协程,但是协程体并不能立即感知自己被取消了,只会执行到ensureActive()才会检测取消信号
  • IO线程上只有IO Start这一句Log会执行,ensureActive()后的代码不再执行

取消有返回值的协程任务Deferred

  • 取消有返回值的协程任务Deferred可以复用取消没有返回值的协程任务Job的取消方式
  • 取消整个协程作用域,也会同时取消其中启动的有返回值的协程任务Deferred
相关推荐
极客先躯1 分钟前
高级java每日一道面试题-2025年12月08日-实战篇[Docker]-如何为 Docker 配置代理?如何为容器配置代理?
java·docker·代理配置的双层架构·docker 守护进程配置代理·为容器配置代理·构建时环境变量·运行时注入环境变量
游戏开发爱好者84 分钟前
使用Fiddler设置HTTPS抓包诊断Power Query网络问题
android·ios·小程序·https·uni-app·iphone·webview
毋语天5 分钟前
Python 常用内置模块详解:日志、随机数、时间、OS 与 JSON
开发语言·python
csdn2015_8 分钟前
java springboot 文件导入,判断第一列的值是否有重复
java·windows·spring boot
右耳朵猫AI11 分钟前
Python技术周刊 2026年第14周
开发语言·python·okhttp
叼烟扛炮20 分钟前
C++ 知识点22 函数模板
开发语言·c++·算法·函数模版
~|Bernard|22 分钟前
四,go语言中GMP调度模型
java·前端·golang
Tisfy24 分钟前
LeetCode 2553.分割数组中数字的数位:模拟(maybe+翻转)——java也O(1)
java·数学·算法·leetcode·题解·模拟·取模
怪祝浙24 分钟前
从简单项目入手Java(学生系统)V6(Web版本 Spring Boot3 MySQL Vue3 MyBatis)
java·spring boot·mysql
求学中--35 分钟前
鸿蒙网络请求从入门到精通:HttpURLConnection+第三方库,GET/POST/文件上传全覆盖
开发语言·php·harmonyos