当前位置: 首页 > news >正文

网站建设人员叫什么哪里有网站建站公司

网站建设人员叫什么,哪里有网站建站公司,宁波建站推广技术公司,免费友情链接网站1、协程的结构化异常管理 如果一个协程抛异常#xff0c;它所在的整个协程树上的其他协程#xff08;向上是父协程到根协程#xff0c;向下是所有后代协程#xff09;都会被取消。因此协程发生异常的后果是十分严重的。 先讲原理#xff0c;再说解决方案。 协程异常的处…1、协程的结构化异常管理 如果一个协程抛异常它所在的整个协程树上的其他协程向上是父协程到根协程向下是所有后代协程都会被取消。因此协程发生异常的后果是十分严重的。 先讲原理再说解决方案。 协程异常的处理与取消采用的是同一套逻辑。只不过取消协程抛出的是一个特殊异常 CancellationException并进行特殊处理以取消协程。而协程异常是抛出其他普通的异常 先查看抛出异常前后父子协程的状态 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)var childJob: Job? nullval parentJob scope.launch {childJob launch {println(Child started)delay(3000)println(Child finished)}delay(1000)throw IllegalStateException(Wrong User!)}// 抛异常之前打印两个协程的状态delay(500)println(isActive: parent - ${parentJob.isActive}, child - ${childJob?.isActive})println(isCancelled: parent - ${parentJob.isCancelled}, child - ${childJob?.isCancelled})// 抛异常之后打印两个协程的状态delay(1500)println(isActive: parent - ${parentJob.isActive}, child - ${childJob?.isActive})println(isCancelled: parent - ${parentJob.isCancelled}, child - ${childJob?.isCancelled})delay(10000) }运行结果 Child started isActive: parent - true, child - true isCancelled: parent - false, child - false Exception in thread DefaultDispatcher-worker-1 java.lang.RuntimeException: Exception while trying to handle coroutine exceptionat kotlinx.coroutines.CoroutineExceptionHandlerKt.handlerException(CoroutineExceptionHandler.kt:33)at ... isActive: parent - false, child - false isCancelled: parent - true, child - true显而易见在父协程抛出异常后父协程被取消连带子协程一起被取消了。 注意应用程序发生未被捕获的异常导致程序崩溃是 Android 的规则而不是普通 JVM 的规则。JVM 中发生异常只会导致线程崩溃而不会导致整个应用崩溃。因此上面的运行结果在崩溃后输出的父子协程状态数据也是正确的、可参考的。 假如让父协程抛出 CancellationException 代替 IllegalStateException你会发现运行结果除了没有异常信息外其余结果是一样的 Child started isActive: parent - true, child - true isCancelled: parent - false, child - false isActive: parent - false, child - false isCancelled: parent - true, child - trueProcess finished with exit code 0这是因为协程的取消与异常处理在底层走的是同一套流程只不过取消处理相对于较为完整的异常处理过程有所简化。 二者的第一个不同是取消的作用方向是单向的只会取消自己与所有下层协程而异常的作用方向是双向的除了取消自己也会向上取消所有上层协程向下取消所有下层协程。 源码证据是在 Job 接口的实现类 JobSupport 中该类作为所有 Job 的父类被继承。当取消子协程时 public open fun childCancelled(cause: Throwable): Boolean {if (cause is CancellationException) return truereturn cancelImpl(cause) handlesException}如果取消的原因是 CancellationException 就返回 true 不继续执行后续的 cancelImpl()该函数内部正是处理取消父协程的代码。 为什么会有这种区别这实际上体现了协程的设计思想。 两个协程之所以被写成父子协程的关系是因为二者在逻辑上有相互包含的关系当然从运行角度二者是并行关系子协程执行的内容通常是父协程任务的子流程。 从正常的逻辑上讲当一个大流程被取消时它内部的子流程也就没用了。因此才会给协程设计这种取消时连带的性质。类似的父协程等待所有子协程完成后再结束也是因为所有子流程完成后整个大流程才算完成所以才有父协程等待子协程的性质。 子协程取消对外部大流程而言可能只是一个正常的事件而已因此子协程的取消不会导致父协程的取消。 但如果子协程抛异常了通常意味着父协程被损坏影响整个大流程。因此子协程抛异常会导致父协程也以该异常为原因而取消。 二者的第二个不同取消有两种方式可以在协程内部抛 CancellationException也可以在协程外部调用 cancel()而异常流程的触发只有一种方式就是在协程内抛异常。 当然协程源码实际上也存在通过 cancel() 触发异常流程的函数 Deprecated(level DeprecationLevel.HIDDEN, message Since 1.2.0, binary compatibility with versions 1.1.x)public fun cancel(cause: Throwable? null): Boolean但是明显能看到该函数是 HIDDEN 的不提供给开发者使用。实际上也很好理解取消函数就触发取消流程不要触发异常流程搞得逻辑混乱。 二者的第三个不同用于取消的 CancellationException 除了用来处理取消协程之外就别无用处而异常流程里抛出的异常对象除了用来取消协程还会把该异常对象暴露给线程。直接体现就是本节举例的协程抛出 IllegalStateException 导致线程崩溃输出 log 信息。 2、CoroutineExceptionHandler 先从一个例子看起 // 示例代码 1 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)scope.launch {try {throw RuntimeException()} catch (e: Exception) {}}delay(10000) }在协程内部将有可能抛出异常的代码用 try-catch 包起来可以捕获异常。但倘若将 try-catch 挪到协程 launch 之外 // 示例代码 2 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)try {scope.launch {throw RuntimeException()}} catch (e: Exception) {}delay(10000) }这个异常就无法被捕获。这种形式类似于 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)try {thread {throw RuntimeException()}} catch (e: Exception) {}delay(10000) }子线程内抛出的异常在主线程去捕获肯定是捕获不到的。对于协程也是一样的像示例代码 2 那样try-catch 只能捕获到协程启动阶段可能抛出的异常也就是 scope.launch 如果抛异常了在它外面的 try-catch 可以捕获到。但对于协程运行阶段的代码也就是 launch 大括号里面的代码它是运行在 Default 线程池的与 try-catch 都不在一个线程中因此无法被捕获。想要捕获协程运行阶段的代码可以像示例代码 1 那样把 try-catch 放在协程内部。 那么真的就没办法在协程外部捕获协程内部的异常了吗答案是有可以使用 CoroutineExceptionHandler将其对象传给最外层协程 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)val handler CoroutineExceptionHandler { _, exception -println(Caught $exception)}// 这个 try-catch 无法捕获到 launch 内的异常try {scope.launch(handler) {throw RuntimeException()}} catch (e: Exception) {}delay(10000) }运行输出结果异常没有抛出 Caught java.lang.RuntimeException使用 CoroutineExceptionHandler 一定要注意的是CoroutineExceptionHandler 对象只能设置给最外层协程这样最外层协程本身及其所有子协程所抛出的异常才可被捕获。如果设置给内层的某个子协程即便是该子协程抛出异常也无法被捕获 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)val handler CoroutineExceptionHandler { _, exception -println(Caught $exception)}scope.launch(/*handler*/) {// 这样还是会抛异常launch(handler) {throw RuntimeException()}}delay(10000) }3、异常结构化管理本质 3.1 UncaughtExceptionHandler 捕获线程异常除了 try-catch还可以使用 UncaughtExceptionHandler fun main() runBlockingUnit {val thread Thread {throw RuntimeException(Thread error!)}// 在线程启动之前设置 UncaughtExceptionHandlerthread.setUncaughtExceptionHandler { _, e - println(Caught $e) }thread.start() }在线程启动之前可以调用 setUncaughtExceptionHandler() 给该线程设置异常捕获运行结果如下 Caught java.lang.RuntimeException: Thread error!除了给单个线程设置也可以给所有线程设置默认的 UncaughtExceptionHandler fun main() runBlockingUnit {Thread.setDefaultUncaughtExceptionHandler { _, e - println(Default caught: $e) }val thread Thread {throw RuntimeException(Thread error!)}thread.setUncaughtExceptionHandler { _, e - println(Caught $e) }thread.start()thread {throw IllegalStateException(Wrong User!)} }运行结果 Caught java.lang.RuntimeException: Thread error! Default caught: java.lang.IllegalStateException: Wrong User!结果显示对于单独设置了 UncaughtExceptionHandler 的线程会使用单独设置的 UncaughtExceptionHandler 捕获异常。未设置的线程会使用默认的 UncaughtExceptionHandler 捕获异常。 对于异常处理要有一些深入的思考。比如对于 Android 应用而言发生未捕获的异常会发生 FC 导致应用崩溃关闭。为了避免 FC 的发生在写代码的时候对于可能发生异常的代码我们会用 try-catch 把它包起来在 catch 代码块中做一些补救工作比如尝试解决问题函数重新执行或者重置变量值等等或重启应用 Todo。而不是单单只为了通过 catch 吞掉这个异常而不发生 FC因为即便不 FC抛异常也意味着你的应用没有正常工作。盲目的吞掉异常让软件在异常状态下继续运行可能会产生无法预料的结果。 对于能提前预料的异常可以使用 try-catch。但是往往会有没有预判的异常出现跑出 catch 的捕获最终被 UncaughtExceptionHandler 接收到。此时线程已经运行结束了没有机会做补救工作了只能做一些善后工作使用通用的解决方案优雅地结束应用。一般有两步收尾工作如记录崩溃日志以及重启或杀死应用 Thread.setDefaultUncaughtExceptionHandler { _, e -// 记录崩溃日志println(Default caught: $e)// 结束或重启应用exitProcess(1) }对于为单个线程设置的 UncaughtExceptionHandler如果该线程只是在内部完成自己的工作其崩溃不影响其他线程那么可以在 UncaughtExceptionHandler 中尝试重启线程。当然一个线程独立完成一件事的场景比较少见。 3.2 CoroutineExceptionHandler 让我们再回头看协程如果没有捕获协程的异常它最终会抛到线程的环境中通过默认的 UncaughtExceptionHandler 可以捕获协程抛出的异常 fun main() runBlockingUnit {// 协程异常会被线程的 DefaultUncaughtExceptionHandler 捕获Thread.setDefaultUncaughtExceptionHandler { _, e -println(Caught in DefaultUncaughtExceptionHandler: $e)}val scope CoroutineScope(EmptyCoroutineContext)// 协程没有捕获异常val job scope.launch {launch {throw RuntimeException()}}job.join() }使用 CoroutineExceptionHandler 为 job 这个协程树添加异常捕获处理可以实现类似于为单个线程设置 UncaughtExceptionHandler 的效果 fun main() runBlockingUnit {Thread.setDefaultUncaughtExceptionHandler { _, e -println(Caught in DefaultUncaughtExceptionHandler: $e)}val scope CoroutineScope(EmptyCoroutineContext)val handler CoroutineExceptionHandler { _, exception -println(Caught in CoroutineExceptionHandler: $exception)}val job scope.launch(handler) {launch {throw RuntimeException()}}job.join() }异常会被 CoroutineExceptionHandler 捕获 Caught in CoroutineExceptionHandler: java.lang.RuntimeException上一节我们说很多任务都是通过多个线程完成的因此在单个线程的 UncaughtExceptionHandler 做重启线程的适用场景并不多。但是我们看 CoroutineExceptionHandler 正是为一个协程树做异常善后工作所用的将一个任务放在协程树中去执行当某一个协程发生异常后可以在 CoroutineExceptionHandler 中重启整个协程树这就具有更广泛的实际意义了。 当然CoroutineExceptionHandler 并不能替代 DefaultUncaughtExceptionHandler 去对整个应用做未知异常的善后工作它只能针对一棵协程树。即便是纯协程应用也是要通过 DefaultUncaughtExceptionHandler 来做通用拦截。 现在再来考虑子协程的异常为什么要交到最外层父协程那里去注册 CoroutineExceptionHandler因为注册 CoroutineExceptionHandler 的目的是善后不管是哪个协程发生了异常都是对整个协程树进行善后。因此设置给最外层协程最方便。 协程异常的结构化管理的本质是针对协程发生的未知异常的善后方案。因为已知异常直接通过在协程内部 try-catch 就可以修复只有未知异常才会走到结构化异常处理流程。 4、async 的异常处理 async 的异常处理与 launch 的大致相同但是因为 async 启动的协程往往需要通过 await() 获取结果会有两点比较明显的差异 如果在 async 内发生异常那么调用 await() 的协程会受到双重影响async 启动的协程在 await() 抛出异常这个异常往往在协程内部就被 try-catch 捕获了。因此 async 作为最外层父协程存在时不会向内部异常抛出到线程世界给最外层的 async 设置 CoroutineExceptionHandler 也就没有作用了 先来看第一点示例代码 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)val handler CoroutineExceptionHandler { _, exception -println(Caught in CoroutineExceptionHandler: $exception)}val job scope.launch(handler) {val deferred async {delay(1000)throw RuntimeException()}launch {// await 也会抛出 async 内抛出的 RuntimeExceptiontry {deferred.await()} catch (e: Exception) {println(Caught in await: $e)}// delay 用来验证 async 抛出的异常触发了结构化取消导致// async - job - 当前协程被取消try {delay(1000)} catch (e: Exception) {println(Caught in delay: $e)}}}job.join() }运行结果 Caught in await: java.lang.RuntimeException Caught in delay: kotlinx.coroutines.JobCancellationException: Parent job is Cancelling; jobStandaloneCoroutine{Cancelling}6463030a Caught in CoroutineExceptionHandler: java.lang.RuntimeException观察结果 因为 await 也会抛出 async 相同的异常所以它的 try-catch 先捕获到异常然后由于 async 抛的异常先让自己也就是 deferred 被取消进而导致父协程 job 被取消进一步导致 delay 所在的协程也要被取消所以 delay 抛出 CancellationExceptionasync 与 await 抛出的异常最终会被最外层协程的 CoroutineExceptionHandler 捕获 以上过程能看出调用 await 的协程实际上受到双重影响一是 await 会抛出与 async 同样的异常导致该协程进入异常处理流程二是 async 抛出异常使得父协程与兄弟协程都会被取消await 所在协程还会触发取消流程 再看第二点差异示例代码 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)val handler CoroutineExceptionHandler { _, exception -println(Caught in CoroutineExceptionHandler: $exception)}scope.async {val deferred async {delay(1000)throw RuntimeException(Error!)}launch(Job()) {try {deferred.await()} catch (e: Exception) {println(Caught in await: $e)}}}delay(3000) }由于 await 抛异常会被 try-catch 捕获因此 async 就不会向线程世界抛出异常。但倘若没有为 await 添加 try-catch其异常还是会被最外层的 async 抛到线程世界需要 CoroutineExceptionHandler fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)val handler CoroutineExceptionHandler { _, exception -println(Caught in CoroutineExceptionHandler: $exception)}scope.async(handler) {val deferred async {delay(1000)throw RuntimeException(Error!)}launch(Job()) {deferred.await()}}delay(3000) }运行结果 Caught in Coroutine: java.lang.RuntimeException: Error!如果不设置 handler 给最外层 async程序就会红字异常信息。所以我才觉得这里它讲的不对它的意思是说 async 就用不到 CoroutineExceptionHandler因为 async 不会向线程世界抛异常。但实际测试结果表明async 不向线程世界抛异常是因为在协程内对 await 用了 try-catch 把异常捕获了。如果没有捕获异常还是需要通过最外层的 CoroutineExceptionHandler 来捕获处理。 5、SupervisorJob SupervisorJob Suppress(FunctionName) public fun SupervisorJob(parent: Job? null) : CompletableJob SupervisorJobImpl(parent)private class SupervisorJobImpl(parent: Job?) : JobImpl(parent) {// 子协程被取消父协程会调用 childCancelledoverride fun childCancelled(cause: Throwable): Boolean false }子协程被取消父协程会调用 childCancelled。但对于 SupervisorJob 而言它直接返回 false 表示子协程发生一般异常非 CancellationException时不取消父协程。也就是说SupervisorJob 不会因为自己的子协程发生普通异常而被取消只有在子协程被取消而发生 CancellationException 时才会被取消。 fun main() runBlockingUnit {val scope CoroutineScope(EmptyCoroutineContext)val supervisorJob SupervisorJob()scope.launch(supervisorJob) {throw RuntimeException(Error!)}delay(100)println(Parent job cancelled: ${supervisorJob.isCancelled})delay(1000) }在抛出异常后仍然会输出 Parent job cancelled: false说明子协程的异常没有导致 supervisorJob 被取消。
http://www.w-s-a.com/news/935681/

相关文章:

  • qq空间怎么做网站做企业平台的网站有哪些
  • 网站的优缺点wordpress手机适配模板中文
  • 福州网站建设H5广告公司简介简短
  • 网站404页面的作用app开发郑州
  • 亚马逊中国网站建设目标网站建设的策划
  • 林州网站建设服务徐州网站建设
  • 如何检测网站死链景德镇网站建设哪家好
  • 旅游网站开发目标天津专业做网站公司
  • 名者观看网站快手小程序
  • 网络架构扁平化windows优化大师好不好
  • 安康养老院收费价格表兰州seo整站优化服务商
  • 网站开发技术方案模板无锡网站建设推荐
  • 自助建站系统注册三维家3d设计软件免费
  • 做seo网站标题重要吗郑州众诚建设监理有限公司网站
  • 建设网站南沙区百度关键词推广怎么做
  • 网站建设公司做销售前景好不好石家庄外贸网站制作
  • windows2008做网站网站首页打开速度
  • 做外贸要做什么网站服装设计图
  • 中山市路桥建设有限公司网站网站开发角色分配权限
  • 加强档案网站建设网站搭建好了不用会不会被攻击
  • 维护网站信息网络建设服务
  • 网站建设策划书模板下载用自己电脑配置服务器做网站
  • 360免费建站空间淘宝数据网站开发
  • 做分销的网站本地dede网站怎么上线
  • 中学网站模板北京管理咨询公司
  • 网站开发用哪个软件方便二级网站建设 管理思路
  • 个人怎么创建网站中国建设银行网站口
  • 跟知乎一样的网站做展示网站步骤
  • 邯郸网站建设效果好wordpress app 加载慢
  • 做app的网站有哪些功能广州自适应网站建设