20-launch与async返回值比较
launch与async构建器都是用来启动新协程,
区别:
launch:返回一个job并且不附带任何结果值
async:返回一个Defrred,Defrred也是一个job,可以使用.await()在一个延期的值上得到它最终的结果
//使用runBlocking函数把主线程包装成主协程,而launch和async则分别启动新的协程,
//新的协程是runBlocking的子协程,runBlocking会等子协程(launch和async分别开辟了一个子协成)执行完毕
@Test
//kotlin中,函数名字可以有空格,但要使用反引号
fun `my test`()= runBlocking {
//launch没有把表达式的执行结果返回
val job1 :Job= launch {
delay(2000)
println("job1 finished")
}
//async可以通过await函数得到协程的执行结果
val job2 = async {
delay(2000)
println("job2 finished")
3000
}
println(job2.await())
}
打印输出如下
job1 finished
job2 finished
3000
21-join与await等待协程的作业
等待一个作业
1,join与await
2,组合并发
我们现在通过launch启动三个协程,第一个协程执行完毕在执行第二个和第三个协程,
上代码
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//使用runBlocking把主线程包装成协程
@Test
fun `my test`()= runBlocking {
val job1 :Job= launch {
delay(4000)
println("job1")
}
job1.join()//通过launch启动的协程可以使用join(),job1执行完后才会执行job2,和job3
val job2 :Job= launch {
delay(2000)
println("job2")
}
val job3 :Job= launch {
delay(2000)
println("job3")
}
}
}
看下输出结果
job1
job2
job3
如果注释掉job1.join()
输出结果如下
job2
job3
job1
再看下async的执行结果
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//使用runBlocking把主线程包装成协程
@Test
fun `my test`()= runBlocking {
val job1 = async {
delay(4000)
println("job1")
}
job1.await()//通过async 启动的协程可以使用await(),job1执行完后才会执行job2,和job3
val job2 = async {
delay(2000)
println("job2")
}
val job3 = async {
delay(2000)
println("job3")
}
}
}
join()和await()都是挂起函数,不会阻塞主线程
22-async组合并发
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//使用runBlocking把主线程包装成协程
@Test
fun `my test`()= runBlocking {
val time = measureTimeMillis {
//统计下这两个函数所花费的时间
var one = doOne()
var two = doTwo()
println("结果${one + two}")
}
println("完成${time}")
}
private suspend fun doOne():Int{
delay(1000)
return 14
}
private suspend fun doTwo():Int{
delay(1000)
return 25
}
}
打印输出
结果39
完成2021
使用async结构性并发
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//使用runBlocking把主线程包装成协程
@Test
fun `my test`()= runBlocking {
val time = measureTimeMillis {
//统计下这两个函数所花费的时间
//子协成会继承父协程的调度器,父协程的调度器在主线程,所以下面这两个协程也是在主线程
var one = async { doOne() }
var two = async { doTwo() }
println("结果${one.await() + two.await()}")
}
println("完成${time}")
}
private suspend fun doOne():Int{
delay(1000)
return 14
}
private suspend fun doTwo():Int{
delay(1000)
return 25
}
}
打印输出
结果39
完成1025
注意:下面的写法是不对的,下面的写法消耗时间也是2秒多
var one = async { doOne() }.await()
var two = async { doTwo() }.await()
println("结果${one + two}")
23 协程的启动模式
启动模式:指定协程启动后的行为
Default:注意立即开始调度不等于立刻执行
Atomic:
async启动的时候会传入一些参数,其中就有启动模式(CoroutineStart )
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
我们看下default启动模式
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//使用runBlocking把主线程包装成协程,rubBlocking会等待子协成全部执行完
@Test
fun `my test`()= runBlocking {
val job = launch (start = CoroutineStart.DEFAULT){
//启动协程,挂起10秒钟
delay(10000)//挂起10秒钟
println("Job finished")
}
//主线程挂起1秒钟
delay(1000)
job.cancel()
}
}
这种情况下并无打印输出,如果注释掉job.cancel(),则会打印输出job finished
Atomic模式
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//使用runBlocking把主线程包装成协程,rubBlocking会等待子协成全部执行完
@Test
fun `my test`()= runBlocking {
val job = launch (start = CoroutineStart.ATOMIC){
//启动协程,挂起10秒钟
delay(10000)//第一个挂起函数,就是第一个挂起点,执行到这里的时候才会响应取消
println("Job finished")
}
//主线程挂起1秒钟
delay(1000)
job.cancel()
}
}
lazy启动模式,这种模式用到的多
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//使用runBlocking把主线程包装成协程,rubBlocking会等待子协成全部执行完
@Test
fun `my test`()= runBlocking {
//协程创建好但没有启动
val job = async (start = CoroutineStart.LAZY){
29
}
//执行一些计算然后启动协程
job.await()//async 使用await()
}
}
UNDISPATCHED模式
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//runBlocking在主线程,当前函数调用栈就是在主线程,所以打印输出是在主线程,虽然调度实在IO
@Test
fun `my test`()= runBlocking {
val job = async (context = Dispatchers.IO,start = CoroutineStart.UNDISPATCHED){
//当前函数调用栈是主线程(在主线程中使用async创建了该协程),runBlocking在主线程里面。
println("Thread${Thread.currentThread().name}")
}
}
}
虽然是Dispatchers.IO调度器,但是打印输出是主线程,如果用Default,则打印子线程。
24-协程的作用域构建器
coroutineScope与runBlocking
runBlocking是常规函数,coroutineScope是挂起函数。它们都会等待其协程体以及所有子协成结束,主要区别在于runBlocking方法会阻塞当前线程来等待,而coroutineScope只是挂起,会释放底层线程用于其他用途
coroutineScope与supervisorScope的区别
coroutineScope:一个协程失败了,所有其他兄弟协程也会被取消。supervisorScope:一个协程失败了,不会影响其他兄弟协程。
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//runBlocking在主线程,当前函数调用栈就是在主线程,所以打印输出是在主线程,虽然调度实在IO
@Test
fun `my test`()= runBlocking {
//把协程包裹在里面,等待子协成执行完毕,作用域才算结束了
coroutineScope {
val job1 = launch {
delay(4000)
println("job1 finished")
}
val job2 = async{
delay(2000)
println("job2 finished")
"job2 result"
//按理说,job2会先执行完,然后在执行job1。但这里抛出了异常,job1不会执行
//job2挂掉了,所有的线程也都完蛋了。
throw IllegalArgumentException()
}
}
}
}
打印输出如下
job2 finished
java.lang.IllegalArgumentException
at com.example.testkotlin1030.ExampleUnitTest$my test$1$1$job2$1.invokeSuspend(ExampleUnitTest.kt:29)
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
at com.example.testkotlin1030.ExampleUnitTest$my test$1.invokeSuspend(ExampleUnitTest.kt:19)
Caused by: java.lang.IllegalArgumentException
at com.example.testkotlin1030.ExampleUnitTest$my test$1$1$job2$1.invokeSuspend(ExampleUnitTest.kt:29)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:235)
at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:167)
at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:408)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:442)
at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:431)
at kotlinx.coroutines.CancellableContinuationImpl.resumeUndispatched(CancellableContinuationImpl.kt:529)
at kotlinx.coroutines.EventLoopImplBase$DelayedResumeTask.run(EventLoop.common.kt:497)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:87)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:61)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:40)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at com.example.testkotlin1030.ExampleUnitTest.my test(ExampleUnitTest.kt:17)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:121)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.base/java.lang.Thread.run(Thread.java:834)
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//runBlocking在主线程,当前函数调用栈就是在主线程,所以打印输出是在主线程,虽然调度实在IO
@Test
fun `my test`()= runBlocking {
supervisorScope {
val job1 = launch {
delay(4000)
println("job1 finished")
}
val job2 = async{
delay(2000)
println("job2 finished")
"job2 result"
//job2 发生异常,job1 照样执行完毕
throw IllegalArgumentException()
}
}
}
}
打印输出
job2 finished
job1 finished
25-Job对象的生命周期
注:协程都完成后指的是协程被取消了或者协程自然结束。协程完成包含两种情况。
26-取消协程
1,取消作用域会取消子协程。
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//通过协程作用域,创建子协程
@Test
fun `my test`()= runBlocking <Unit>{//必须指定泛型类型为<Unit>,不然报错
//创建协程作用域
val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
delay(1000)
println("job1")
}
scope.launch {
delay(1000)
println("job2")
}
}
}
我们会发现运行这段代码并没有任何的打印。
我们前面讲过,runBlocking 会等待它的子协程执行完毕。但是我们发现这里并没有,原因是CoroutineScope创建的子协程,并没有继承runBlocking的协程上下文,它有自己的上下文。如果我们要等待两个子协程完成,需要这样改
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
//通过协程作用域,创建子协程
@Test
fun `my test`()= runBlocking <Unit>{//必须指定泛型类型为<Unit>,不然报错
//创建协程作用域
val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
delay(1000)
println("job1")
}
scope.launch {
delay(1000)
println("job2")
}
delay(2000)//主线程挂起两秒钟,等待子协成执行完毕
}
}
现在运行就有打印输出了。
我们取消作用域就会取消子协成,我们取消下
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun `my test`()= runBlocking <Unit>{//必须指定泛型类型为<Unit>,不然报错
//创建协程作用域
val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
delay(1000)
println("job1")
}
scope.launch {
delay(1000)
println("job2")
}
delay(100)
scope.cancel()//通过scope作用域启动的两个子协程都会取消
delay(2000)//主线程挂起两秒钟
}
}
等待100毫秒肯定不会有任何打印输出,因为通过CoroutineScope这个作用域创建的子协成都被取消掉了。
coroutineScope与CoroutineScope的区别
coroutineScope为协程的作用域构建器,会自带CoroutineScope
CoroutineScope自己构建的协程作用域
相同点是可以进行结构化编程,创建和取消子协程
2,被取消的子协程,并不会影响其余兄弟协程
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun `my test`() = runBlocking<Unit> {//必须指定泛型类型为<Unit>,不然报错
//创建协程作用域
val scope = CoroutineScope(Dispatchers.IO)
val job1 = scope.launch {
delay(1000)
println("job1")
}
val job2 = scope.launch {
delay(1000)
println("job2")
}
delay(100)
job1.cancel()//job1被取消,job2仍然会执行的
delay(2000)//主线程挂起两秒钟
}
}
打印输出job2
3,协程通过抛出一个特殊的异常,CancellationException来处理取消操作
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun `my test`() = runBlocking<Unit> {//必须指定泛型类型为<Unit>,不然报错
val job1 = GlobalScope.launch {
delay(1000)
println("job1")
}
//没有job1.join()这句代码,不会打印输出job1
//GlobslScope有自己的作用域,这种方式启动的协程,没有继承父协程
//上下文和作用域,外面的父协程不会等待job1执行结束。
job1.join()
}
}
job1 的取消
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun `my test`() = runBlocking<Unit> {//必须指定泛型类型为<Unit>,不然报错
val job1 = GlobalScope.launch {
delay(1000)//这里一直是挂起,调用cancel之后会抛出异常
println("job1")
}
job1.cancel()//会抛一个异常,但是异常被静默处理掉了
job1.join()
}
}
如果我们不想静默处理掉
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun `my test`() = runBlocking<Unit> {//必须指定泛型类型为<Unit>,不然报错
val job1 = GlobalScope.launch {
try {
delay(1000)
println("job1")
}catch (e:Exception){
e.printStackTrace()
}
}
job1.cancel()//因为使用了try catch,异常不会再被静默处理
job1.join()
}
}
job1,cancel()也可以写成job.cancel(CancellationException(“取消”))
kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job="coroutine#2":StandaloneCoroutine{Cancelling}@16c6b661
at kotlinx.coroutines.JobSupport.cancel(JobSupport.kt:1603)
at kotlinx.coroutines.Job$DefaultImpls.cancel$default(Job.kt:183)
at com.example.testkotlin1030.ExampleUnitTest$my test$1.invokeSuspend(ExampleUnitTest.kt:26)
at _COROUTINE._BOUNDARY._(CoroutineDebugging.kt:46)
at com.example.testkotlin1030.ExampleUnitTest$my test$1$job1$1.invokeSuspend(ExampleUnitTest.kt:20)
Caused by: kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job="coroutine#2":StandaloneCoroutine{Cancelling}@16c6b661
at kotlinx.coroutines.JobSupport.cancel(JobSupport.kt:1603)
at kotlinx.coroutines.Job$DefaultImpls.cancel$default(Job.kt:183)
at com.example.testkotlin1030.ExampleUnitTest$my test$1.invokeSuspend(ExampleUnitTest.kt:26)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277)
at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:87)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:61)
at kotlinx.coroutines.BuildersKt.runBlocking(Unknown Source)
at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:40)
at kotlinx.coroutines.BuildersKt.runBlocking$default(Unknown Source)
at com.example.testkotlin1030.ExampleUnitTest.my test(ExampleUnitTest.kt:17)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:121)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.base/java.lang.Thread.run(Thread.java:834)
也可以这么写
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun `my test`() = runBlocking<Unit> {//必须指定泛型类型为<Unit>,不然报错
val job1 = GlobalScope.launch {
try {
delay(1000)
println("job1")
}catch (e:Exception){
e.printStackTrace()
}
}
job1.cancelAndJoin()//cancel join合并到一起。
}
}
4,所有kotlinx.corotines中的挂起函数(withContext,delay等)都是可以取消的。
5,CPU密集型任务的取消
我们有这么个任务,每隔500毫秒就会打印一次。
package com.example.testkotlin1030
import kotlinx.coroutines.*
import org.junit.Test
import org.junit.Assert.*
import kotlin.system.measureTimeMillis
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun `my test`() = runBlocking<Unit> {//必须指定泛型类型为<Unit>,不然报错
val startTime = System.currentTimeMillis()
//CPU密集型任务在Default调度器中运行,在主线程中通过isActive取消不了
val job = launch(Dispatchers.Default){
var nextPrintTime = startTime
var i = 0
while(i < 5 && isActive){//有没有isActive会有很大区别,有,打印三次,没打印5次
//每秒打印消息两次
if(System.currentTimeMillis() >= nextPrintTime){
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L)
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消一个作业并且等待它结束
println("main: Now I can quit.")
}
}
如果没有&& isActive,依然会打印5次,没有取消成功。
但是,只要调用了cancle函数,isActive就会变为false。所以我们增加&& isActive判断。如果被取消了,isActive就会变成false,这样一来,while循环就会结束。只会打印三次
6,CPU密集型任务取消-ensureActive
//ensureActive(),如果job处于非活跃状态,这个方法会立即抛出异常。
@Test
fun `test cancel cpu task by ensureActive`() = runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default){
var nextPrintTime = startTime
var i = 0
while(i < 5){
ensureActive()//取消之后会抛出一个被静默处理掉的异常(CancellationException),依然利用了isActivity函数
if(System.currentTimeMillis() >= nextPrintTime){
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L)
println("main: I'm tired of waiting!")
job.cancelAndJoin()
println("main: Now I can quit.")
}
6,CPU密集型任务取消-yield
//yield函数会检查所在协程的状态,如果已经取消,则抛出CancellationException予以响应。
//此外,它还会尝试出让线程的执行权,给其他协程提供执行机会。
//如果要处理的任务属于:
//1) CPU 密集型,2) 可能会耗尽线程池资源,3) 需要在不向线程池中添加更多线程的前提下允许线程处理其他任务,那么请使用 yield()。
@Test
fun `test cancel cpu task by yield`() = runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default){
var nextPrintTime = startTime
var i = 0
while(i < 5){
yield()//如果已经取消,会抛出一个被静默处理的异常
if(System.currentTimeMillis() >= nextPrintTime){
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L)
println("main: I'm tired of waiting!")
job.cancelAndJoin()
println("main: Now I can quit.")
}
}
只打印三次
7,协程取消的副作用
我们抛出一个异常,导致下面的代码没法执行,但下面的代码很最重要,必须执行,比如释放资源的代码,这时候可以用finally
//在 finally 中释放资源
@Test
fun `test release resources`() = runBlocking {
val job = launch {
try {
//搞一千个协程,每个协程delay500毫秒
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.")
}
只执行了三次 println(“job: I’m sleeping $i …”),说明被取消了。但即使被取消了,依然打执行了
println(“job: I’m running finally”)
8,kotlin中标准函数use释放资源
use函数:该函数只能被实现了Closeable的对象使用,程序结束的时候会自动调用close方法,适合文件对象
@Test
fun `test use function`() = runBlocking {
//不用use函数如何关闭
val br = BufferedReader(FileReader("D:\\I have a dream.txt")) //打开文件读取,我D盘有该文件
with(br) { //对br中的属性和方法直接进行操作
var line: String?
try{
while (true) {
line = readLine() ?: break //读取一行数据,若为空则退出循环
println(line) //打印读取的数据
}
}finally{
close() //关闭文件读取,最好协程try{}catch(){}finally{}的样子。
}
}
//我们使用use函数关闭 use会自动关闭,不需要手动处理
BufferedReader(FileReader("D:\\I have a dream.txt")).use {
var line: String?
while (true) {
//it 就是BufferedReader对象
line = it.readLine() ?: break //读取一行数据,若为空则退出循环
println(line) //打印读取的数据
}
}
//另外,最简单的读取文件的方式
println(BufferedReader(FileReader("D:\\I have a dream.txt")).readText()) //最简单的读取文件的方法
}
9,不能取消的任务
当我们调用 job.cancelAndJoin() 取消后,协程就进入取消状态,在finaly里释放资源,但是在finally里释放资源的时候又遇到了挂起函数(实际开发中在取消资源的时候发起网络请求等),这是候的挂起函数是无法执行的,没办法让我们的协程挂起。如果让 job.cancelAndJoin()的取消不影响finally中的挂起函数执行,则使用 withContext(NonCancellable)
@Test
fun `test cancel with NonCancellable`() = runBlocking {
val job = launch {
try {
repeat(1000) { i ->
println("job: I'm sleeping $i ...")
delay(500L)
}
} finally {
withContext(NonCancellable) {
println("job: I'm running finally")
delay(1000L)
//如果没有 withContext(NonCancellable) ,下面不会打印输出
println("job: And I've just delayed for 1 sec because I'm non-cancellable")
}
}
}
delay(1300L) // 延迟⼀段时间
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消该作业并等待它结束
println("main: Now I can quit.")
}
}
注:不能取消的任务不一定只用在finally里面。如果协程是个常驻任务,也可以使用 withContext(NonCancellable) 。
10,超时任务的处理
很多情况下我们取消一个协程的理由是它有可能超时,比如我们发起一个网络请求,5秒钟还没有完成,我们就把它取消。
withTimeoutOrNull通过返回null来进行超时操作,从而代替抛出一个异常
package com.dongnaoedu.kotlincoroutine
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import kotlinx.coroutines.withTimeoutOrNull
import org.junit.Test
/**
*
* @author ningchuanqi
* @version V1.0
*/
class CoroutineTest05 {
//超时任务,
//很多情况下取消一个协程的理由是它有可能超时。
@Test
fun `test deal with timeout`() = runBlocking {
//1300毫秒还没执行完,就结束协程
withTimeout(1300L) {
//1000次操作,1300毫秒肯定不够
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
}
上面代码会抛出一个异常,但是这个异常没有被静默处理掉(文字为红色的异常)。,但有的时候我们超时不想抛出异常,而是返回一个默认值,我们可以下面这样写
//withTimeoutOrNull 通过返回 null 来进行超时操作,从而替代抛出一个异常
@Test
fun `test deal with timeout return null`() = runBlocking {
//1300毫秒还没执行完,就结束协程
val result = withTimeoutOrNull(1300L) {
//1000次操作,1300毫秒肯定不够
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // 在它运行得到结果之前取消它,
}
println("Result is $result")
}
}
如果没有超时,正常执行完,会打印输出Result is done
如果超时,会打印输出Result is null,
当然我们不想返回null,我们可以使用空合并操作符。
fun `test deal with timeout return null`() = runBlocking {
//1300毫秒还没执行完,就结束协程
val result = withTimeoutOrNull(1300L) {
//1000次操作,1300毫秒肯定不够
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
"Done" // 在它运行得到结果之前取消它,
}?:“哈哈哈”
println("Result is $result")
}
}
如果超时,会打印输出Result is 哈哈哈,