一:协程的取消 1.取消协程作用域CoroutineScope/coroutineScope 会取消它里面的所有子协程
coroutineScope 与 CoroutineScope 的区别
不同点:
coroutineScope是协程的作用域构建器函数 参数需要传入一个匿名CoroutineScope对象 CoroutineScope的一个函数方法
CoroutineScope 自己创建了一个协程作用域对象
相同点:
在里面都可以通过lanuch asynic 创建启动子协程 取消子协程
private fun cancelAllSubCoroutine() {
runBlocking <Unit>{
/** suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R
* 是协程的作用域构建器函数 参数需要传入一个匿名CoroutineScope对象
* 是 CoroutineScope的一个函数方法
* */
coroutineScope({
val job0 = launch { LogUtil.e("job0 was executed.") }
val job00 = async { LogUtil.e("job00 was executed.") }
job0.cancel() //输出 job00 was executed.
})
/**
* CoroutineScope(context: CoroutineContext): CoroutineScope
* 自己创建了一个协程作用域对象
* */
//取消协程作用域CoroutineScope会取消它关联的所有子协程job1 job11
val coroutineScope1 = CoroutineScope(Dispatchers.Default)
val job1= coroutineScope1.launch {
delay(1000)
LogUtil.e("job1 was executed.")
}
val job11= coroutineScope1.launch {
delay(1000)
LogUtil.e("job11 was executed.")
}
val coroutineScope2 = CoroutineScope(Dispatchers.Default)
val job2 = coroutineScope2.launch {
delay(1000)
LogUtil.e("job2 was executed.")
}
delay(100)
coroutineScope1.cancel()
delay(1000)
//这里是取消协程作用域coroutineScope1,会取消它关联的所有子协程job1 job11 输出最总结果: job2 was executed.
}
}
2.被取消的子协程并不会影响其余兄弟协程
//协程job1的取消不会影响job11协程的执行
private fun cancelOneSubCoroutine() {
runBlocking <Unit>{
coroutineScope({
val job1 = launch {
delay(1000)
LogUtil.e("job1 was executed.")
}
val job11= launch {
delay(1000)
LogUtil.e("job11 was executed.")
}
delay(100)
job1.cancel()
delay(1000)
// 输出最总结果: job11 was executed.
})
}
}
3.取消异常
协程通过抛出一个特殊的异常 CancellationException 来处理取消操作。
所有kotlinx.coroutines中的挂起函数(withContext、delay等)都是可取消的。
在调用 cancel()函数取消协程时, 可以传入一个 CancellationException 实例来提供更多关于本次取消的详细信息
如果您不构建新的 CancellationException 实例将其作为参数传入的话,会创建一个默认的 CancellationException
private fun cancellationException(){
runBlocking <Unit>{
/**
* GlobalScope 创建协程对象 继承 CoroutineScope
* */
val job3 = GlobalScope.launch {
try{ delay(1000)
LogUtil.e("job3 was executed.")
} catch (e: Exception) {
e.printStackTrace()
LogUtil.e("job3 cancel exception .${e.toString()}") //输出 job3 cancel exception .java.util.concurrent.CancellationException: 取消协程job3的执行
}
}
delay(100)
//在调用 cancel()函数取消协程时, 可以传入一个 CancellationException 实例来提供更多关于本次取消的详细信息
//如果您不构建新的 CancellationException 实例将其作为参数传入的话,会创建一个默认的 CancellationException
job3.cancel(CancellationException("取消协程job3的执行"))
job3.join() //协程取消了 无法输出 job3 was executed
}
}
二:CPU密集型任务取消协程
1.CPU密集型任务取消协程 isActive是一个可以被使用在CoroutineScope中的扩展属性,检查Job是否处于活跃状态。 如果Job 调用cancelAndJoin()函数 取消协程的执行,那么isActive=false ,协程就非活跃状态不执行退出了
2.ensureActive(),如果job处于非活跃状态,
这个方法会立即抛出异常CancellationException,退出协程的执行
ensureActive()查看源码就是isActive: Boolean属性
public fun Job.ensureActive(): Unit { if (!isActive) throw getCancellationException() }
3.yield
yield()函数会检查当前所在协程的状态,如果该协程(job.cancelAndJoin())已经取消,则抛出异常CancellationException予以响应。
此外,它还会尝试出让线程的执行权,给其他协程提供执行机会。
如果要处理的任务属于:
1) CPU 密集型,
2) 可能会耗尽线程池资源,
3) 需要在不向线程池中添加更多线程的前提下允许线程处理其他任务,那么请使用 yield()。
runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default){
var nextPrintTime = startTime
var i = 0
while(i < 10){
yield()
if(System.currentTimeMillis() >= nextPrintTime){
LogUtil.e("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L)
LogUtil.e("main: I'm tired of waiting!")
job.cancelAndJoin()
LogUtil.e("main: Now I can quit.")
}