Kotlin

协程的取消和超时

2020-11-14  本文已影响0人  码农修行之路
协程的取消和超时.png

取消协程

协程取消失效问题

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关闭资源

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("取消协程后")
}

运行不可取消的代码块

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("协程取消执行后")
}

超时

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
}

异步超时和资源

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块中返回资源 避免资源泄漏
谢谢亲们的关注支持   记得点赞哦!

上一篇下一篇

猜你喜欢

热点阅读