协程入门(二):挂起与取消

协程的挂起与线程的休眠

协程经过delay(timeMillis)实现挂起,线程经过sleep(timeMillis)实现休眠。可是挂起和休眠存着差别性api

协程挂起与线程休眠bash

相同点:微信

1.都能达到堵塞的目的;

2.在该状态下(挂起/休眠)都能被终止执行(取消/中断);

3.取消/中断时都会抛出异常
复制代码

不一样点:async

1.线程休眠会直接堵塞当前线程,该线程没法再执行其它操做,可是协程挂起不会堵塞当前线程,线程上的其它协程能够继续运行;

2.delay操做只能在协程环境使用,sleep在协程环境和普通线程环境均可使用

3.线程中断抛出的是InterruptedException异常,协程中断抛出的是CancellationException异常
复制代码

协程的取消与线程的中断

协程的取消与线程的中断,2者十分相似 :spa

比较 调用api 能被直接取消的状态 事务处理时 抛出的异常
协程 cancel() delay 没法被直接取消 CancellationException
线程 interrupt() sleep 没法被直接取消 InterruptedException

相同点:线程

1.都是须要处于挂起/休眠状态,才可以直接取消/中断;code

2.处于事务处理时,没法直接被取消/中断;cdn

不一样点在于:协程

1.协程存在父协程的概念,可是线程没有啥所谓的父线程。取消父协程后,会自动取消其全部的子协程;事务

2.协程在挂起时被取消,会抛出CancellationException异常,线程在休眠时被中断,会抛出InterruptedException

示例

能被直接取消/中断

该协程job在执行cancel的时候,处于delay状态,可以被直接取消

fun cancel() = runBlocking {
        val job = launch {
            println("1")
            delay(3000)
            //被cancel后,下边再也不执行
            println("2")
        }
        delay(100)
        job.cancel()
        println("3")
    }
    
打印出:
    1
    3
复制代码

该线程thread在执行interrupt的时候,处于sleep状态,可以被直接中断

fun threadInterrupt() {
        val thread = thread {
            println("1")
            Thread.sleep(1 * 1000)
            println("2")
        }
        //抛出InterruptedException但不影响后面执行
        thread.interrupt()
        Thread.sleep(3 * 1000)
        println("3")
    }
打印出:
1
3
复制代码

处于事务处理,没法被直接取消/中断

协程在执行cancel()后,内部的扩展属性isActive会置为false,代码中能够结合该变量跳出事务处理的流程。 以下例子中的while()判断,假设没有结合iaActive,由于当前协程处于事务处理,不是delay挂起状态,没法直接被cancel, 须要等待事务处理完毕。

为了达到cancel后尽早结束协程,能够结合isActive进行判断。

fun cancel() = runBlocking {
        val job = launch(Dispatchers.Default) {
            var nextPrintTime = System.currentTimeMillis()
            var i = 0
            //isActive是协程扩展属性,cancel()后变为false
            while (i < 5 && isActive) {
                // 一个执行计算的循环,只是为了占用 CPU
                if (System.currentTimeMillis() >= nextPrintTime) {
                    println("job: I'm sleeping ${i++} ...")
                    // 每秒打印消息两次
                    nextPrintTime += 500L
                }
            }
        }
        delay(100)
        job.cancel()
        println("end")
    }
    
结合isActive进行中断判断,打印出:
job: I'm sleeping 0 ... end 假设没有结合isActive进行判断,打印出: job: I'm sleeping 0 ...
end
job: I'm sleeping 1 ... job: I'm sleeping 2 ...
job: I'm sleeping 3 ... job: I'm sleeping 4 ...
复制代码

线程使用interrupt()结合isInterrupted变量,达到终端操做的目的,效果与分析同协程cancel()结合isActive相似:

fun threadInterrupt() {
        val thread = thread {
            var nextPrintTime = System.currentTimeMillis()
            var i = 0
            //isInterrupted是线程属性,interrupt()后变为false
            while (i < 5 && !Thread.currentThread().isInterrupted) {
                // 一个执行计算的循环,只是为了占用 CPU
                if (System.currentTimeMillis() >= nextPrintTime) {
                    println("thread: I'm sleeping ${i++} ...")
                    // 每秒打印消息两次
                    nextPrintTime += 500L
                }
            }
        }
        thread.interrupt()
        Thread.sleep(3 * 1000)
        println("end")
    }
    
结合isInterrupted进行中断判断,打印出:
thread: I'm sleeping 0 ... end 假设没有结合isInterrupted进行判断,打印出: thread: I'm sleeping 0 ...
end
thread: I'm sleeping 1 ... thread: I'm sleeping 2 ...
thread: I'm sleeping 3 ... thread: I'm sleeping 4 ...
复制代码

取消父协程会致使子协程也跟着取消

父协程手动调用cancel()或者异常结束,会当即取消它的全部子协程。

下例cancel()父协程job,其neibu内部的子协程都会跟着也cancel掉。

fun cancel() = runBlocking {
        val job = launch {
            launch(Dispatchers.Default) {
                var nextPrintTime = System.currentTimeMillis()
                var i = 0
                //isActive是协程扩展属性,cancel()后变为false
                while (i < 5 && isActive) {
                    // 一个执行计算的循环,只是为了占用 CPU
                    if (System.currentTimeMillis() >= nextPrintTime) {
                        println("child launch: I'm sleeping ${i++} ...")
                        // 每秒打印消息两次
                        nextPrintTime += 500L
                    }
                }
            }

            async(Dispatchers.IO) {
                var nextPrintTime = System.currentTimeMillis()
                var i = 0
                //isActive是协程扩展属性,cancel()后变为false
                while (i < 5 && isActive) {
                    // 一个执行计算的循环,只是为了占用 CPU
                    if (System.currentTimeMillis() >= nextPrintTime) {
                        println("child async: I'm sleeping ${i++} ...")
                        // 每秒打印消息两次
                        nextPrintTime += 500L
                    }
                }
            }
        }
        delay(1000)
        job.cancel()
        println("cancel parent job")
        delay(10 * 1000)
    }
    
打印出:
child launch: I'm sleeping 0 ... child async: I'm sleeping 0 ...
cancel parent job
复制代码

微信公众号

相关文章
相关标签/搜索