王学岗Kotlin协程(二)————协程的启动与取消

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 哈哈哈,

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值