美文网首页
Kotlin-协程的取消关键技术分析

Kotlin-协程的取消关键技术分析

作者: 蒋斌文 | 来源:发表于2021-06-15 12:08 被阅读0次

Kotlin-协程的取消关键技术分析

image-20210615111549352
fun main() = runBlocking {
    val myJob = GlobalScope.launch {
        repeat(200) { i ->
            println("hello: $i")

            delay(500)

        }
    }

    delay(1100)
    println("hello world")

    myJob.cancel()
    myJob.join()


    println("welcome")
}

RUN> 🏄🏄🏄🏄🏄🏄

hello: 0
hello: 1
hello: 2
hello world
welcome

Process finished with exit code 0
image-20210615112101098

下面来瞅一下它的cancel()方法的说明:

image-20210615112244628
public fun cancel(cause: CancellationException? = null)
cause: CancellationException? = null 可空类型,默认值为null
Thrown by cancellable suspending functions if the Job of the coroutine is cancelled while it is suspending. It indicates normal cancellation of a coroutine. It is not printed to console/log by default uncaught exception handler. See CoroutineExceptionHandler
public actual typealias CancellationException = java.util.concurrent.CancellationException
image-20210615112533715

可以显示的指定一下这个参数,如下:

fun main() = runBlocking {
    val myJob = GlobalScope.launch {
        repeat(200) { i ->
            println("hello: $i")

            delay(500)

        }
    }

    delay(1100)
    println("hello world")

    myJob.cancel(CancellationException("just a try"))
    myJob.join()


    println("welcome")
}
myJob.cancel(CancellationException("just a try"))
myJob.join()
这俩一定是成对的出现,其中为啥一定得要调用join()
是因为Cancel调用之后其协程并不会立马就取消,
所以这个join0需要等待一下协程彻底取消

那既然这俩是需要成对来编写的,那有没有一种简化的方法能代替上面两句代码呢?答案是肯定的,如下:

fun main() = runBlocking {
    val myJob = GlobalScope.launch {
        repeat(200) { i ->
            println("hello: $i")

            delay(500)

        }
    }

    delay(1100)
    println("hello world")

    myJob.cancelAndJoin()


    println("welcome")
}
/**
Cancels the job and suspends the invoking coroutine until the cancelled job is complete.
This suspending function is cancellable and always checks for a cancellation of the invoking coroutine's Job. If the Job of the invoking coroutine is cancelled or completed when this suspending function is invoked or while it is suspended, this function throws CancellationException.
In particular, it means that a parent coroutine invoking cancelAndJoin on a child coroutine that was started using launch(coroutineContext) { ... } builder throws CancellationException if the child had crashed, unless a non-standard CoroutineExceptionHandler is installed in the context.
This is a shortcut for the invocation of cancel followed by join.
*/
public suspend fun Job.cancelAndJoin() {
    cancel()
    return join()
}

取消任务并且挂起这个调用的协程直到取消的任务真正的执行完。

image-20210615115128324

kotlinx.coroutines包下的所有挂起函数都是可取消的,他们会检查协程的取消状态,当取消时就会抛出CancellationException异常。不过,如果协程正在处于某个计算过程当中,并且没有检查取消状态,那么它就是无法被取消的。

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime

        var i = 0

        while (i < 20) {
            if (System.currentTimeMillis() >= nextPrintTime) {//CPU空转轮询
                println("job: I am sleeping ${i++}")
                nextPrintTime += 500L
            }
        }
    }

    delay(1300)
    println("hello world")

    job.cancelAndJoin()
    println("welcome")
}

RUN> 🏄🏄🏄🏄🏄🏄

job: I am sleeping 0
job: I am sleeping 1
job: I am sleeping 2
hello world
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
welcome

Process finished with exit code 0

呃,居然协程木有取消成功,这是为啥呢?其实这里要论证的就是刚才的这个理论:

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

那怎么能让其正常取消呢?这里又得先来看一下理论:

image-20210615120019312

有两种方式可以让计算代码变为可取消的:

1、周期性地调用了一个挂起函数,该挂起函数会检测取消状态,比如说使用yield函数。

2、显示地检查取消状态。

fun main() = runBlocking {
    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {
        var nextPrintTime = startTime

        var i = 0

        while (isActive) {
            if (System.currentTimeMillis() >= nextPrintTime) {
                println("job: I am sleeping ${i++}")
                nextPrintTime += 500L
            }
        }
    }

    delay(1300)
    println("hello world")

    job.cancelAndJoin()
    println("welcome")
}

RUN> 🏄🏄🏄🏄🏄🏄

job: I am sleeping 0
job: I am sleeping 1
job: I am sleeping 2
hello world
welcome

Process finished with exit code 0
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
public val CoroutineScope.isActive: Boolean
    get() = coroutineContext[Job]?.isActive ?: true
// CoroutineScope的一个扩展属性
image-20210615115810054

第一个程序delay(500) 为什么可以被取消

delay(500)
image-20210615120458109

相关文章

  • Kotlin-协程的取消关键技术分析

    Kotlin-协程的取消关键技术分析 RUN> ??????hello: 0hello: 1hello: 2hel...

  • kotlin 协程之取消协程

    取消作用域会取消它的子协程。 被取消的子协程并不会影响其余兄弟协程。 协程通过抛出一个特殊的异常Cancellat...

  • Kotlin-协程核心库分析-Job父子取消

    父Job取消时如何取消子Job 父协程完成结束 我们看下子协程如何被取消的。首先我们需要知道 子协程启动的时候会放...

  • 破解 Kotlin 协程(5) - 协程取消篇

    关键词:Kotlin 协程 协程取消 任务停止 协程的任务的取消需要靠协程内部调用的协作支持,这就类似于我们线程中...

  • 一学就会的协程使用——基础篇(三)初遇取消

    1. 引言 协程支持取消,也就是说,启动一个协程后而且在协程结束前已经不希望协程再执行代码了,可以对协程进行取消。...

  • kotlin协程的取消

    对于可被取消的协程和不可被取消的协程。 https://www.kotlincn.net/docs/referen...

  • Kotlin-协程

    开启线程的方式 使用 runBlocking 顶层函数。一般用于测试。 使用 GlobalScope 单例对象。也...

  • kotlin-协程

    Why 简化异步代码的编写。 执行严格主线程安全确保你的代码永远不会意外阻塞主线程,并增强了代码的可读性。 提升代...

  • Kotlin-协程

    协程的定义 协程可以理解为一种轻量级的线程。协程和线程的区别是线程是依靠操作系统的调度才能实现不同线程之间的切换的...

  • Kotlin-协程

    1.什么是协程? 是一套基于线程的API框架,重点:还是基于线程。 2.协程有什么用? 可以灵活地切换线程,用同步...

网友评论

      本文标题:Kotlin-协程的取消关键技术分析

      本文链接:https://www.haomeiwen.com/subject/lllzeltx.html