首发于公众号: DSGtalk1989
30.协程取消与超时
-
如何取消我们需要的取消
上文中出现了
cancel
方法来进行取消的操作,但是过程中,我们的例子我怕会有误解,我们直接在协程还没有启动的时候取消了它,我们来看下是否可以启动了一会儿还可以暂停呢。这个和我们java中熟知的Thread
比较不一样,首先Thread
在很久以前就不跟你玩cancel
了,其次Thread
只要起来了,就停不下来了。不像协程,起都没起,还能够取消。我们变一种取消的方式再来看看。
fun main() = runBlocking { val startTime = System.currentTimeMillis() val job = launch { var nextPrintTime = startTime var i = 0 while (i < 5) { // 一个执行计算的循环,只是为了占用 CPU // 每秒打印消息两次 if (System.currentTimeMillis() >= nextPrintTime) { println("I'm sleeping ${i++} ...") nextPrintTime += 500L } } } println("runBlocking start") delay(10) job.cancel() println("delay end") }
我们只是
delay
了10ms,照理来说cancel
应该立马让线程停掉,但是依然把launch
的协程给完全跑完了。因为我们并没有在协程中去检测现在是否协程还活着。这个跟Thread
中的interrupt
方法比较像,中断过后线程的信号量会改变,我们需要时刻的去关注线程当前是否被打断了,并且时刻的捕捉InterruptException
。前文中之所以我们可以操作
cancel
是因为函数delay
是支持取消的。This suspending function is cancellable.
所以一旦遇到如上的情况,我们就需要在遍历循环或者是计算的过程中去判断一下
isActive
这个字段,协程是否还活着。同时调用yield
方法也能实现。
-
finally语句关闭资源
针对上面的情况,一旦我们取消了协程,而我们希望协程中使用到的一些资源可以释放掉,我们就使用
try finally
。val job = launch { try { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } finally { println("I'm running finally") } }
-
执行无法取消的代码
有时候我们不希望我们的部分代码段可以被取消,无论是各种可以取消的挂起函数,还是其他的计算函数。
val job = launch { repeat(1000) { i -> println("I'm sleeping $i ...") withContext(NonCancellable){ delay(500L) } } }
这样一来,
delay
就无法取消了。 -
超时异常
java中,我们通常需要去中断一个线程的最主要的原因就是超时了,这边一样,在kotlin中,为我们提供了一个非常方便的方法
withTimeout
,一旦超过了规定的时间,就会自动抛出TimeoutCancellationException
,这是一个CancellationException
的子类,所以他也是需要挂起函数支持被取消才行。我们也可以使用
withTimeoutOrNull
,这个函数一旦超过了指定的时间,会立马返回一个null
,而不是抛出异常。withTimeout(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } } val result = withTimeoutOrNull(1300L) { repeat(1000) { i -> println("I'm sleeping $i ...") delay(500L) } "Done" // 协程会在输出这个消息之前被取消 } println("Result is $result")
Kotlin学习笔记之 13 基础操作符run、with、let、also、apply
网友评论