kotlin之协程(五),launch 函数以及协程的取消与超时
目录
kotlin之协程(一),线程,进程,协程,协程可以替换线程吗?
kotlin之协程(二),Kotlin协程是什么、挂起是什么、挂起的非阻塞式
kotlin之协程(三),开始创建协程,launch,withContext
kotlin之协程(四),协程的核心关键字suspend
kotlin之协程(五),launch 函数以及协程的取消与超时
kotlin之协程(六),协程中的 async和launch的区别以及runBlocking
kotlin之协程(七),协程中relay、yield 区别
如果对于协程需要精细控制,比如说,一个用户也许关闭了一个启动了协程的界面,那么现在协程的执行结果已经不再被需要了,这时,它应该是可以被取消的
launch 函数
launch 函数定义
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job
launch 是个扩展函数,接受3个参数,前面2个是常规参数,最后一个是个对象式函数,这样的话 kotlin 就可以使用以前说的闭包的写法:() 里面写常规参数,{} 里面写函数式对象的实现,就像上面的例子一样
GlobalScope.launch(Dispatchers.Unconfined) {...}
我们需要关心的是 launch 的3个参数和返回值 Job:
- CoroutineContext - 可以理解为协程的上下文,在这里我们可以设置 CoroutineDispatcher 协程运行的线程调度器,有 4种线程模式:
- Dispatchers.Default
- Dispatchers.IO -
- Dispatchers.Main - 主线程
- Dispatchers.Unconfined - 没指定,就是在当前线程
不写的话就是 Dispatchers.Default 模式的,或者我们可以自己创建协程上下文,也就是线程池,newSingleThreadContext 单线程,newFixedThreadPoolContext 线程池
-
CoroutineStart - 启动模式,默认是DEAFAULT,也就是创建就启动;还有一个是LAZY,意思是等你需要它的时候,再调用启动
- DEAFAULT - 模式模式,不写就是默认
- ATOMIC -
- UNDISPATCHED
- LAZY - 懒加载模式,你需要它的时候,再调用启动
-
block - 闭包方法体,定义协程内需要执行的操作
-
Job - 协程构建函数的返回值,可以把 Job 看成协程对象本身,协程的操作方法都在 Job 身上了
- job.start() - 启动协程,除了 lazy 模式,协程都不需要手动启动
- job.join() - 等待协程执行完毕
- job.cancel() - 取消一个协程
- job.cancelAndJoin() - 等待协程执行完毕然后再取消
创建一个协程
创建该 launch
函数返回了一个可以被用来取消运行中的协程的 Job
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
取消
val job = launch {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L) // 延迟一段时间
println("main: I'm tired of waiting!")
job.cancel() // 取消该作业
job.join() // 等待作业执行结束
println("main: Now I can quit.")
程序执行后的输出如下:
job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
main: Now I can quit.
一旦 main 函数调用了 job.cancel,我们在其它的协程中就看不到任何输出,因为它被取消了
超时
在实践中绝大多数取消一个协程的理由是它有可能超时。 当你手动追踪一个相关 Job的引用并启动了一个单独的协程在延迟后取消追踪,这里已经准备好使用 withTimeout 函数来做这件事。 来看看示例代码:
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
运行后得到如下输出:
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1300 ms
这里我们看到了TimeoutCancellationException异常,这异常是因为超时导致的异常取消
解决这个问题也很简单通过withTimeout
的withTimeoutOrNull
函数,代码示例:
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // 在它运行得到结果之前取消它
}
println("Result is $result")
运行后得到如下输出:
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
Result is null
这样就没有抛出异常了