Kotlin协程的取消机制:深入理解和优雅实现

ops/2025/1/13 10:38:43/

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点

Kotlin协程提供了一种高效的方式来处理并发和异步任务。在协程的生命周期管理中,取消协程是一项重要的操作。本文将深入探讨Kotlin协程的取消机制,介绍除了直接使用Jobcancel方法之外的其他方式,并提供优雅的实现策略。

1. 协程取消的基本概念

在Kotlin协程中,取消协程是一个协作过程。当外部请求取消协程时,协程需要定期检查自己的取消状态,并在适当的时候退出。这种设计允许协程在取消时进行清理工作,比如关闭资源、保存状态等。

1.1 检查取消状态

协程可以通过以下方式检查自己是否被取消:

  • isActive:如果协程没有被取消,返回true
  • isCancelled:如果协程被取消了,返回true

1.2 取消协程

取消协程可以通过调用Jobcancel方法来实现。这会标记协程为取消状态,但不会立即停止协程。协程需要定期检查自己的取消状态,并在适当的时候退出。

2. 优雅的取消协程

2.1 使用CompletableDeferred

CompletableDeferred是一个特殊的协程构建器,它允许你手动完成或取消一个协程。它常用于需要等待某个异步操作完成或取消的场景。

kotlin">import kotlinx.coroutines.*fun main() = runBlocking {val deferred = CompletableDeferred<Unit>()launch {try {deferred.await() // 等待某个条件} catch (e: CancellationException) {println("Deferred was cancelled")} catch (e: Exception) {println("An error occurred: ${e.message}")}}delay(1000L)deferred.cancel() // 取消等待println("main: Now I can quit.")
}

在这个示例中,我们通过CompletableDeferred来控制协程的取消。当外部条件满足时,我们可以取消等待,并通过try-catch块来处理取消和异常。

2.2 使用isActive检查

在协程内部,你可以通过检查isActive属性来决定是否继续执行。如果isActive返回false,协程应该停止执行。

kotlin">import kotlinx.coroutines.*fun main() = runBlocking {val job = launch {try {while (isActive) {// 执行任务delay(500L)}} catch (e: CancellationException) {println("Job was cancelled")}}delay(1000L)job.cancel() // 取消协程job.join() // 等待协程结束println("main: Now I can quit.")
}

在这个示例中,我们在协程内部使用while (isActive)来检查协程是否被取消,并在取消时通过try-catch块来处理取消。

2.3 使用ensureActive

ensureActive是一个函数,如果当前协程被取消了,它会抛出CancellationException。你可以在协程的关键点调用它来确保协程仍然活跃。

kotlin">import kotlinx.coroutines.*fun main() = runBlocking {val job = launch {try {repeat(1000) { i ->ensureActive() // 确保协程仍然活跃println("job: I'm sleeping $i ...")delay(500L)}} catch (e: CancellationException) {println("Job was cancelled")}}delay(1300L)job.cancel() // 取消协程job.join() // 等待协程结束println("main: Now I can quit.")
}

在这个示例中,我们在协程的关键点调用ensureActive来确保协程仍然活跃。如果协程被取消了,ensureActive会抛出CancellationException,并通过try-catch块来处理取消。

2.4 使用yield

yield函数可以让出协程的执行权,允许其他协程运行。它也可以用于检查协程是否应该继续执行。

kotlin">import kotlinx.coroutines.*fun main() = runBlocking {val job = launch {try {while (isActive) {yield() // 让出执行权并检查取消状态println("job: I'm sleeping ...")delay(500L)}} catch (e: CancellationException) {println("Job was cancelled")}}delay(1000L)job.cancel() // 取消协程job.join() // 等待协程结束println("main: Now I can quit.")
}

在这个示例中,我们在协程内部使用yield来让出执行权,并检查协程是否应该继续执行。如果协程被取消了,yield会抛出CancellationException,并通过try-catch块来处理取消。

2.5 使用CoroutineScope的取消

如果你在CoroutineScope中启动协程,你可以通过取消整个CoroutineScope来间接取消所有在其中启动的协程。

kotlin">import kotlinx.coroutines.*fun main() = runBlocking {val scope = CoroutineScope()val job = scope.launch {try {repeat(1000) { i ->println("job: I'm sleeping $i ...")delay(500L)}} catch (e: CancellationException) {println("Job was cancelled")}}delay(1000L)scope.cancel() // 取消整个协程作用域scope.join() // 等待协程作用域结束println("main: Now I can quit.")
}

在这个示例中,我们在CoroutineScope中启动协程,并在需要时取消整个作用域。这会间接取消所有在作用域中启动的协程。

2.6 使用select协程构建器

select构建器可以用来构建基于选择的协程逻辑,其中可以包含取消操作。

kotlin">import kotlinx.coroutines.*fun main() = runBlocking {val job = launch {select<Unit> {onCancel {println("Coroutine was cancelled")}onTimeout(1000) {println("Timeout occurred")}}}delay(1000L)job.cancel() // 取消协程job.join() // 等待协程结束println("main: Now I can quit.")
}

在这个示例中,我们使用select构建器来构建基于选择的协程逻辑。我们监听取消事件,并在协程被取消时打印消息。

3. 常见理解误区

3.1 误区1:取消协程会立即停止

取消协程并不会立即停止它。协程需要定期检查自己的取消状态,并在适当的时候退出。

3.2 误区2:取消协程会导致异常

取消协程不会抛出异常。如果协程没有正确处理取消状态,它可能会继续运行,直到自然结束或遇到其他错误。

3.3 误区3:cancelAndJoin会立即停止协程

cancelAndJoin方法会取消协程并等待它完成。但是,如果协程没有检查取消状态,它仍然不会立即停止。

4. 结论

理解协程的取消机制对于编写高效、健壮的异步代码至关重要。通过使用CompletableDeferredisActive检查、ensureActiveyieldCoroutineScope的取消以及select协程构建器,你可以优雅地管理和取消协程,确保资源被正确释放,同时避免不必要的异常处理。

通过本文的介绍,你应该对Kotlin协程中的取消机制有了更深入的理解。在实际开发中,合理地使用这些机制,可以大大提高代码的健壮性和可维护性。


欢迎关注我的公众号AntDream查看更多精彩文章!

AntDream


http://www.ppmy.cn/ops/108795.html

相关文章

(六十七)第 10 章 内部排序(快速排序)

示例代码 qSort.h // 快速排序实现头文件#ifndef Q_SORT_H #define Q_SORT_H#include "errorRecord.h"#define MAX_SIZE 20 #define NUM 8typedef int KeyType; typedef int InfoType;typedef struct {KeyType key;InfoType otherInfo; } RecType;typedef struct {Re…

Android Studio打开Modem模块出现:The project ‘***‘ is not a Gradle-based project

花了挺长时间处理该问题&#xff0c;特记录如下&#xff1a;1.背景&#xff1a; 在Android studio 下导入一个新增的modem模块&#xff0c;如MPSS.DE.3.1.1\modem_proc\AAA, 目的是看代码方便一些&#xff0c;可以自由搜索各种关键字。但导入该项目时出现了如下错误&#xff1a…

ansible--yaml

语法 #列表 fruits:-Apple-Orange-banada########################################### fruits顶格写&#xff0c;下面的参数空两个空格&#xff0c;必须得对齐 #字典martin: name: Mysqlenvironoment: dd################################ 第一行的冒号号有空格&#xff0c;…

计算机网络练级第一级————认识网络

目录 网络搁哪&#xff1f; 网络的发展史&#xff08;了解&#xff09; 独立模式&#xff1a; 网络互联&#xff1a; 局域网时期&#xff1a; 广域网时期&#xff1a; 什么是协议 TCP/IP五层/四层模型 用官话来说&#xff1a; 我自己的话来说 第一层应用层&#xff1…

websocket协议与http协议

WebSocket 协议和 HTTP 协议都是用于网络通信的协议&#xff0c;但它们在设计目标、通信方式和应用场景上有一些关键的区别。以下是它们的主要区别&#xff1a; 1. 设计目标 HTTP&#xff1a; 设计目标&#xff1a;HTTP 旨在为万维网提供一种标准化的请求/响应机制&#xff0c…

九月五日(k8s配置)

一、安装环境 环境准备&#xff1a;&#xff08;有阿里云&#xff09; k8s-master 192.168.1.11 k8s-node1 192.168.1.22 k8s-node2 192.168.1.33 二、前期准备 在k8s-master主机 [rootk8s-master ~]# vim /etc/hosts …

HarmonyOS开发之路由跳转

文章目录 一、路由跳转模式与实例1.router.pushUrl2.router.replaceUrl3.router.back 一、路由跳转模式与实例 跳转模式 有点类似于vue的路由跳转 router.pushUrl 保留路由栈&#xff0c;保留当前的页面&#xff1b;router.replaceUrl 销毁当前页面&#xff0c;跳转一个新的页…

网站安全需求分析与安全保护工程

网站安全威胁与需求分析 网站安全概念 网站&#xff1a;是基于B/S技术架构的综合信息服务平台&#xff0c;主要提供网页信息及业务后台对外接口服务。 网站安全性&#xff1a; 机密性&#xff1a;网站信息及相关数据不被授权查看或泄露完整性&#xff1a;网站信息及数据不能…