一.协程的启动
1.协程构建器
launch与async构建器都用来启动新协程:
launch,返回一个Job并且不附带任何结果值。
async,返回一个Deferred,Deferred也是一个job,可以使用await在一个延期的值上得到它的最终结果。
fun main() {
testCoroutine();
}
// runBlocking 主协程 他会等子协程任务执行完再关闭,在此之前都是堵塞状态
fun testCoroutine()= runBlocking {
//async的await函数会延时返回一个结果值,launch不能
val job = launch {
delay(2000)
println( "launch finished")
}
val job1 = async {
delay(2000)
println( "async finished")
"async result"
}
println(job1.await())
}
打印结果:
launch finished
async finished
async result
2.launch与async的顺序执行
我们经常会碰到这样一个情况,在调取一个接口获取信息后再调取其他接口,这样按顺序调取。
1.launch
fun main() {
testCoroutine1();
}
//runBlocking 主协程 他会等子协程任务执行完再关闭,在此之前都是堵塞状态
fun testCoroutine1()= runBlocking {
//都是异步任务
val launch = launch {
delay(100)
println("One")
}
//join是一个挂起函数 他会暂停协程直到所有的任务完成
launch.join()
val launch1 = launch {
delay(100)
println("Two")
}
val launch2 = launch {
delay(100)
println("Three")
}
}
打印结果:
2.async
fun main() {
testCoroutine1();
}
//runBlocking 主协程 他会等子协程任务执行完再关闭,在此之前都是堵塞状态
fun testCoroutine1()= runBlocking {
val async = async {
delay(100)
println("One")
}
async.await()
val async1 = async {
delay(100)
println("Two")
}
val async2 = async {
delay(100)
println("Three")
}
}
打印结果:
3.async结构化并发
fun main() {
TestSync()
}
//runBlocking 主协程 他会等子协程任务执行完再关闭,在此之前都是堵塞状态
fun TestSync()= runBlocking {
val time = measureTimeMillis {
//这样可以同时开始两个子协程执行并发任务
val doOne = async {doOne() }
val doTwo = async {doTwo() }
//这里是调用的同步方法等全部执行完后进行运算
print("The result:${doOne.await() + doTwo.await()} \n")
}
print("Completed in $time ms")
}
suspend fun doOne():Int {
delay(1000)
return 20
}
suspend fun doTwo():Int {
delay(1000)
return 25
}
打印结果
The result:45
Completed in 1027 ms
这里可以看出我在两个获取值的函数里各延时1秒,但由于结构化并发的原因整个方法执行时间就只用了一秒。
4.协程的启动模式
DEFAULT:协程创建后,立即开始调度,在调度前如果协程被取消,其将直接进入取消 响应的状态。
fun testStartMode() = runBlocking<Unit> {
val job = launch(start = CoroutineStart.DEFAULT) {
var i = 0
while (true){
i++
}
println("finished.")
}
delay(1000)
job.cancel()
}
ATOMIC:协程创建后,立即开始调度,协程执行到第一个挂起点之前不响应取消。
fun testStartMode() = runBlocking<Unit> {
val job = launch(start = CoroutineStart.ATOMIC) {
//....
//在执行到第一个挂起点之前取消无效
delay(1000)
println("finished.")
}
}
LAZY:只有协程被需要时,包括主动调用协程的start、join或者await等函数时才会开始 调度,如果调度前就被取消,那么该协程将直接进入异常结束状态。
fun testStartMode() = runBlocking<Unit> {
//惰性启动
val job = async (start = CoroutineStart.LAZY) {
29
}
//执行一些计算
//再启动
job.start()
}
UNDISPATCHED:协程创建后立即在当前函数调用栈中执行,直到遇到第一个真正挂起
fun testStartMode() = runBlocking<Unit> {
//在哪个线程中执行?
val job = launch(context = Dispatchers.IO, start = CoroutineStart.UNDISPATCHED) {
println("thread:"+Thread.currentThread().name)
}
}
UNDISPATCHED?启动模式会立即在当前线程执行,既可能会在主线程执行。
5.协程作用域
//作用域构建器
//runBlocking是常规函数,而coroutineScope是挂起函数。
//它们看起来很类似,它们都会等待其协程体以及所有子协程结束。
//主要区别在于runBlocking方法会阻塞当前线程来等待,而coroutineScope只是挂起,会释放底层线程用于其他用途。
fun testCoroutineScope () = runBlocking {
//作用域的构建器 这里继承了父作用域 上下文也会继承
//coroutineScope
//一个协程失败了,所有其他兄弟协程也会被取消
coroutineScope {
val job1 = launch {
delay(400)
println("job1 finished.")
}
val job2 = launch {
delay(200)
println("job2 finished.")
throw IllegalArgumentException()
}
}
//supervisorScope
//一个协程失败了,不会影响其他兄弟协程
supervisorScope {
val job1 = launch {
delay(400)
println("job1 finished.")
}
val job2 = launch {
delay(200)
println("job2 finished.")
throw IllegalArgumentException()
}
}
//这里因为继承了runBlocking的作用域coroutineScope和supervisorScope执行完后runBlocking才会完成
}
6.Job对象的生命周期
对于每一个创建的协程(通过launch或者async),会返回一个Job实例,该实例是协程的唯一标示,并且负责管理协程的生命周期。 一个任务可以包含一系列状态: 新创建(New)、活跃(Active)、完成中(Completing)、已完成(Completed)、取消(Cancelling)和已取消(Cancelled)。 虽然我们无法直接访问这些状态,但是我们可以访问Job的属性:isActive、isCancelled和isCompleted。
如果协程处于活跃状态,协程运行出错或者调用 job.cancel() 都会将当前任务置为取消中 (Cancelling) 状态 (isActive = false, isCancelled = true)。 ?当所有的子协程都完成后,协程会进入已取消 (Cancelled) 状态,此时 isCompleted = true。
二.取消协程
1.协程的取消
1.取消作用域会取消它的子线程
fun testScopeCancel()= runBlocking {
/*这里又建了一个协程作用域,有新的上下文,而下面的两个子协程并不算runBlocking
的子协程,所以在下面如果不延时就直接程序执行完关闭了*/
val scope= CoroutineScope(Dispatchers.Default)
scope.launch {
delay(1000)
print("job 1")
}
scope.launch {
delay(1000)
print("job 2")
}
delay(100)
//scope作用域对象 他一取消作用域内的子协程都会取消
scope.cancel()
delay(2000)
}
CoroutineScope(Dispatchers.Default)与coroutineScope的区别在于前者有自己的作用域和上下文,而coroutineScope会继承父协程的作用域与上下文
2.被取消的子协程并不会影响其余兄弟协程
fun testScopeCancel()= runBlocking {
/*这里又建了一个协程作用域,有新的上下文,而下面的两个子协程并不算runBlocking的子协程,
所以在下面如果不延时就直接程序执行完关闭了*/
val scope= CoroutineScope(Dispatchers.Default)
val launch = scope.launch {
delay(1000)
print("job 1")
}
scope.launch {
delay(1000)
print("job 2")
}
delay(100)
//子协程取消不会影响到其他的子协程
launch.cancel()
delay(2000)
}
3.抛出异常来中断协程
fun main() {
testCancellationException()
}
fun testCancellationException()= runBlocking {
val launch = GlobalScope.launch {
try {
//这里因为协程被取消了,会抛出一个异常
delay(1000)
print("job 1")
}catch (e :Exception){
e.printStackTrace()
}
}
launch.cancel()
//launch .cancel(CancellationException("取消"))这里也可以抛出异常
//能让runBlocking等待GlobalScope.launch执行完毕
launch.join()
}
异常打印:
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutine{Cancelling}@306b9c53
2.CPU密集型任务取消
//CPU密集型运算的协程不能取消,可以借用协程生命周期让运算停止后取消
fun testCancelCpuTaskByIsActive()= runBlocking{
val startTime = System.currentTimeMillis()
val launch = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0;
//yield也会抛一个异常,还有会出让一部分的执行权(就算出让线程的资源,但这不意味着他不会再执行了,他还会在执行)
//如果要处理的任务属于:
//1) CPU 密集型,2) 可能会耗尽线程池资源,3) 需要在不向线程池中添加更多线程的前提下允许线 程处理其他任务,那么请使用 yield()。
//yield()
//ensureActive()此方法为抛异常结束协程 如果job处于非活跃状态,这个方法会立即抛出异常。
//ensureActive()
// isActive是一个可以被使用在CoroutineScope中的扩展属性,检查Job是否处于活跃状态。
//当协程调用cancel方法时 isActive为false 运算停止
while (i < 5 && isActive) {
if (System.currentTimeMillis() >= nextPrintTime) {
println("job:I'm sleeping ${i++}")
nextPrintTime += 500
}
}
}
delay(1500)
println("main: I'm tired of waiting!")
launch.cancelAndJoin()
println("main: Now I can quit.")
}
3.协程取消后释放资源
1.通过finally释放资源
//在 finally 中释放资源
fun testReleaseResources() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
} finally {
println("job: I'm running finally")
}
}
delay(1300L) // 延迟?段时间
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消该作业并且等待它结束
println("main: Now I can quit.")
}
2。use函数
//use函数:该函数只能被实现了Closeable的对象使用,程序结束的时候会自动调用close方法,适合文件对象
fun testReleaseResources() = runBlocking {
BufferedReader(FileReader("D:\\I have a dream.txt")).use {
var line: String?
while (true) {
line = it.readLine() ?: break //读取一行数据,若为空则退出循环
println(line) //打印读取的数据
}
}
}
4.在finally中执行协程中断后的任务
fun testReleaseResources() = runBlocking {
val launch = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
//一般协程取消后finally中的任务不会继续执行
} finally {
//协程执行后可以在finally释放资源
println("job: I'm running finally")
//要这样才能再取消协程后继续finally的任务 协程中也可以使用
withContext(NonCancellable){
println("job: I'm running finally")
delay(1000L)
println("Job:And I've just delayed for 1 sec because I'm non-cancellable")
}
}
}
}
5.处理超时任务
fun main() {
testDealWithTimeout()
}
fun testDealWithTimeout()= runBlocking{
//处理耗时任务用这个方法
//超过1300毫秒算这个任务超时
val withTimeout = withTimeoutOrNull(1300) {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
"Done"
}?:"Jack"
//执行成功返回 Done 超时返回 Jack
println("Result is $withTimeout ...")
}
|