首页 > 其他分享 >Kotlin协程的取消机制:深入理解和优雅实现

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

时间:2024-09-10 16:52:55浏览次数:3  
标签:协程 取消 Kotlin 优雅 catch job println main

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

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

1. 协程取消的基本概念

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

1.1 检查取消状态

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

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

1.2 取消协程

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

2. 优雅的取消协程

2.1 使用CompletableDeferred

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

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,协程应该停止执行。

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。你可以在协程的关键点调用它来确保协程仍然活跃。

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函数可以让出协程的执行权,允许其他协程运行。它也可以用于检查协程是否应该继续执行。

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来间接取消所有在其中启动的协程。

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构建器可以用来构建基于选择的协程逻辑,其中可以包含取消操作。

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查看更多精彩文章!

标签:协程,取消,Kotlin,优雅,catch,job,println,main
From: https://blog.51cto.com/u_16841384/11972119

相关文章

  • 优雅处理封装返回
    前言大家写代码可能会使用try...catch处理异常,当然springmvc架构中各层会出现大量的try{...}catch{...}finally{...}代码块,不仅有大量的冗余代码,而且还影响代码的可读性。下面推荐大家这样处理,既方便代码也显得更加规范优雅,真的香的不行。推荐理由:代码复制到项目中通过简单......
  • C++20 协程:异步编程的新纪元
    C++20引入了协程(coroutines),这是一种全新的异步编程模型,使得编写异步代码变得更加简洁和直观。本文将详细介绍C++20协程的概念、功能演变及其在实际项目中的应用。通过本文,你将了解到协程的基本原理、语法和如何利用协程来简化异步编程。1.协程的概念协程(coroutine)是......
  • 如何把网页的公式优雅地拷贝到word中:数学公式识别神器—Mathpix Snip
    这个编辑器其实在把chatgpt的公式粘贴到word中时就已经使用了,用的是网页版。现在下载了软件(但是好像一个月试用期过后得收费?但是就目前来说,体验感真的超级好)把公式复制粘贴转成mathtype公式可以截取电脑屏幕上的图像,如果图像上面有公式的话,就会识别,之后可以复制latex格式(第二个......
  • Kotlin快速入门,全是干货没有废话
    Kotlin基础数据类型在kotlin中,变量使用var关键字声明,常量使用val关键字声明。变量声明可以通过变量名:数据类型的形式显式声明,也可以通过自动推导的方式声明。//通过【变量名:数据类型】的形式声明变量varname:String="Soria"valage:Int=18//可以不显式声明......
  • android从java/kotlin层传递bitmap给jni并使用其像素
    一、概述在做jni开发的时候,有些情况下会直接通过java/kotlin层传递bitmap给jni,并取出其数据进行利用。例如:OpenGLES绘制纹理、保存像素图片等。二、代码示例1.在cmake中引入可以操作jni层BitmapInfo的libjnigraphics-landroid2.导入头文件#in......
  • 从Workload中优雅隔离Pod
    线上集群中,业务跑着跑着,突然发现有个Pod上出现大量错误日志,其他的Pod是正常的,该如何处理呢?直接删除Pod?这样不便于保留现场,可能会影响判断问题的根因让业务方忍一会,先排查下问题?会被喷死最好的方案是既让Pod停止接收流量,又保留Pod思路:停止接收流量停止接收流量这个动......
  • Android 使用拦截器结合协程实现无感知的 Token 预刷新方案
    背景在应用中,我们通常使用Token作为用户认证的凭证。为了安全起见,Token一般设置较短的有效期,并通过refreshToken进行续期。传统的做法是当服务端返回Token过期的响应(如401)时,再进行刷新,但这种方式可能导致用户体验不佳(如突然的登录状态丢失、请求失败等)。网上关于A......
  • android kotlin基础复习—for while do...while
    1、新建一个文件kt:2、循环的几种用法:forwhiledo...whilefor:println("----for使用-----")valitems=listOf("apple","banana","kiwi")for(iteminitems){println(item)}for(indexinitems.indic......
  • Python教程(十七):协程、 asyncio与 aiohttp【异步IO】
    文章目录专栏列表1.异步IO的基本概念1.1同步与异步1.2协程1.3asyncio1.4aiohttp2.携程2.1定义协程2.2运行协程3.asyncio3.1事件循环解释3.2获取文件示例3.2并发获取文件示例4.aiohttp:异步HTTP客户端/服务器4.1安装aiohttp4.2异步HTTP请求4.3异......
  • ServiceStage集成Sermant实现应用的优雅上下线
    作者:聂子雄华为云高级软件工程师摘要优雅上下线旨在确保服务在进行上下线操作时,能够平滑过渡,避免对业务造成影响,保证资源的高效利用。Sermant基于字节码增强的技术实现了应用优雅上下线能力,应用发布与运维平台ServiceStage通过集成Sermant使得应用在进行持续发布时实现无侵入式地......