美文网首页Kotlin
协程的取消和超时

协程的取消和超时

作者: 码农修行之路 | 来源:发表于2020-11-14 21:40 被阅读0次
协程的取消和超时.png

取消协程

协程取消失效问题

  • cancel()方法调用后 马上返回而不是等协程结束后再返回,所以协程并不一定马上停止 为了保证协程执行完再执行后续代码 此时就需要调用join()方法阻塞等待
fun main() = runBlocking {    
    val job = launch {        
        repeat(1000) {            
            println(" 打印低 $it 次 ")
            delay(500L)
        }    
    }    
    delay(2000L)
    println(" 等待2秒后啦! ")
    //job.cancel() // 取消协程
    //job.join()    // 阻塞 等待协程执行完毕
    job.cancelAndJoin()
}
  • 协程中执行循环操作 如果没有判读取消的条件 那么此协程是不会停止的
fun main() = runBlocking {    val startTime = System.currentTimeMillis()
    val job = launch(Dispatchers.Default) {        
        var i = 0        
        while (true) {
            println(" 一直打印数据 ${i++} ")
        }
    }    
    delay(2000L)
    job.cancelAndJoin()
}

解决取消协程失效问题:

  1. 添加判断循环停止的条件 while (i < 10)
  2. 添加挂起函数 只要下面调用job.cancelAndJoin() 所有的挂起都会被停止
while (true) {
    delay(100L)
    println(" 一直打印数据 ${i++} ")
}
  1. yieid()的使用
suspend fun main() {
    val job = GlobalScope.launch {        
        var num = 0        
        while (true) {
            //try {                
            yield()
            //} catch (e: Exception) {// StandaloneCoroutine was cancelled            
            //    println(" 捕捉的异常 ${e.message} ")                    
            //}            
            println(" 开始执行到 ${num++} ")
        }
    }    
    println(" 取消前 是否还在活动 ${job.isActive} 是否取消 ${job.isCancelled} ")
    job.cancelAndJoin()
    println(" 取消后 是否还在活动 ${job.isActive} 是否取消 ${job.isCancelled} ")
    delay(1000L)
}
  1. 添加是否取消或者是否活动的判断
while (isActive) {
    println(" 开始执行到 ${num++} ")
}

########finally关闭资源

  • 课取消的挂起函数在取消时会抛出异常 CancellationException 可以用常用的方式进行处理
    例如:try {...} finally {...} 表达式和 kotlin 的 use 函数都可用于在取消协程时执行回收操作
fun main() = runBlocking {    
    val job = launch {       
        try {
            repeat(1000) {                
                println(" 执行到第 $it ")
                delay(500L)
            }       
        } finally {
            println(" finally 执行到 可以执行资源回收操作  ")
        }
     }    
    delay(1300L)
    println("延迟等待结束")
    // job.cancel()    
    //job.join()   
    // job.cancelAndJoin()// 等待所有启动的协程回收操作完成后再继续执行之后的代码   
    println("取消协程后")
}

运行不可取消的代码块

  • 如果finally中使用挂起函数 将会抛出异常 CancellationException 因为此时协程已经被取消
    也就是在finally中先调用挂起函数 会导致挂起函数之后的代码不会被执行
  • 当然上述问题也并不是什么问题 因为良好的关闭操作通常都是非阻塞的且不会设计挂起函数 直接执行代码就好
    如果非要添加挂起函数 那么就需要吧挂起函数包裹在 withContext(NonCancellable){...}代码块中 这样挂起函数后面的操作代码就能正常执行啦
fun main() = runBlocking {    
    val job = launch {       
        try {
            repeat(1000) {                
                delay(200L)
                println(" 执行到第 $it ")
            }        
        } finally {
            // 如果在finally中先调用挂起函数 会导致之后的输出不会执行            
            // 如果需要在取消的协程中调用挂起函数可以使用withContext(NonCancellable){...}代码块            
            /*delay(100L)            
            println(" finally 代码块执行 ")*/            
            withContext(NonCancellable) {                
                delay(100L)
                println(" finally 代码块执行 ")
            }        
        }
    }   
    println("延迟执行前")
    delay(2000L)
    println("延迟执行后")
    job.cancelAndJoin()
    println("协程取消执行后")
}

超时

  • 超时会抛出异常
    Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1300 ms
fun main() = runBlocking {   
    withTimeout(1300L) {        
        repeat(1000) { i ->            
            println("I'm sleeping $i ...")
            delay(500L)
        }    
    }    
}

怎样避免抛出异常:

  1. 可以通过try{...}catch(e:TimeoutCancellationException){...}代码块捕捉异常 进行处理
  2. 可以通过withTimeoutOrNull 函数以便在超时时返回 null 而不是抛出异常
fun main() = runBlocking {    
    val result = withTimeoutOrNull(1300L) {        
        repeat(1000) { i ->            
            println("I'm sleeping $i ...")
            delay(500L)
        }    
    }    
    println(" 结果 $result ")
}
var acquired = 0
class Resource {
    init {
        acquired++
    } // Acquire the resource
    fun close() {
       acquired--
    } // Release the resource
}

异步超时和资源

  • withTimeout 是异步执行的 对于其块内代码 可能发生在任何时间 如果在 withTimeout() {} 返回之前 在块内打开或获取一些需要的在块外释放的资源
var acquired = 0
class Resource {
   init { acquired++ } // Acquire the resource
   fun close() { acquired-- } // Release the resource
}
fun main() {
    runBlocking {
        repeat(100_000) { // Launch 100K coroutines
            launch { 
               val resource = withTimeout(60) { // Timeout of 60 ms
                    delay(50) // Delay for 50 ms
                   Resource() // Acquire a resource and return it from withTimeout block     
                }
                resource.close() // Release the resource
            }
        }
    }
   // Outside of runBlocking all coroutines have completed
   println(acquired) // Print the number of resources still acquired
}

上述例子 有可能还会获取到acquired > 0 的值 为什么呢?原因很简单: 就是资源创建和资源关闭有可能不同步

fun main() = runBlocking {    
    repeat(100_000) {
        // Launch 100K coroutines       
        launch {            
            var resource: Resource? = null // Not acquired yet            
            try {
                withTimeout(60) { // Timeout of 60 ms                    
                    delay(50) // Delay for 50 ms                    
                    resource = Resource() // Store a resource to the variable if acquired                
                }                
                // We can do something else with the resource here            
            } finally {            
              resource?.close() // Release the resource if it was acquired            
            }
        }   
    }    
    // Outside of runBlocking all coroutines have completed    
    println(acquired) // Print the number of resources still acquired
}

上述例子就是把对资源的引用存储到变量 而不是从withTimeout块中返回资源 避免资源泄漏
谢谢亲们的关注支持   记得点赞哦!

相关文章

  • 协程的取消和超时

    取消协程 协程取消失效问题 cancel()方法调用后 马上返回而不是等协程结束后再返回,所以协程并不一定马上停止...

  • Kotlin协程(4)✔️管理协程

    等待协程结束超时设置取消协程   协程的管理比线程的管理要简单的多。 等待协程结束   前面提到过 join 函数...

  • 协程

    一、取消与超时 1.线程取消 2.Job Job是标准库中启动协程后返回的对象,代表着协程本次作业。我们可以判断协...

  • Kotlin学习之协程的取消与超时

    这一部分包含了协程的取消与超时。 取消协程的执行 在一个⻓时间运行的应用程序中,你也许需要对你的后台协程进行细粒度...

  • Kotlin 协程(二) -协程取消与超时

    协程一:Kotlin 协程 (一)[https://mp.weixin.qq.com/s?__biz=Mzg3NT...

  • Kotlin学习笔记之 30 协程取消与超时

    首发于公众号: DSGtalk1989 30.协程取消与超时 如何取消我们需要的取消上文中出现了cancel方法来...

  • kotlin协程的取消

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

  • kotlin 协程之取消协程

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

  • Kotlin协程的取消与超时

    一、取消协程的执行 main 函数调用了 job.cancel ,就可以取消协程。也可以使 Job 挂起的函数 c...

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

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

网友评论

    本文标题:协程的取消和超时

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