<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                [TOC] ## 異常處理 這部分內容包括異常處理以及取消異常。我們已經知道當協程被取消的時候會在掛起點拋出[CancellationException](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html),并且它在協程機制中被忽略了。但是如果一個異常在取消期間被拋出或多個子協程在同一個父協程中拋出異常將會發生什么? ### 異常的傳播 協程構建器有兩種風格:自動的傳播異常([launch](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html) 以及 [actor](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html))或者將它們暴露給用戶([async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html) 以及 [produce](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html))。前者對待異常是不處理的,類似于 Java 的 `Thread.uncaughtExceptionHandler`,而后者依賴用戶來最終消耗異常,比如說,通過 [await](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html)或 [receive](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html)([produce](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html) 以及 [receive](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html)在[通道](https://www.kotlincn.net/docs/reference/coroutines/channels.html)中介紹過)。 可以通過一個在 [GlobalScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html) 中創建協程的簡單示例來進行演示: ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { val job = GlobalScope.launch { println("Throwing exception from launch") throw IndexOutOfBoundsException() // 我們將在控制臺打印 Thread.defaultUncaughtExceptionHandler } job.join() println("Joined failed job") val deferred = GlobalScope.async { println("Throwing exception from async") throw ArithmeticException() // 沒有打印任何東西,依賴用戶去調用等待 } try { deferred.await() println("Unreached") } catch (e: ArithmeticException) { println("Caught ArithmeticException") } } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-01.kt)獲取完整代碼。 這段代碼的輸出如下([調試](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/coroutine-context-and-dispatchers.md#debugging-coroutines-and-threads)): ```text Throwing exception from launch Exception in thread "DefaultDispatcher-worker-2 @coroutine#2" java.lang.IndexOutOfBoundsException Joined failed job Throwing exception from async Caught ArithmeticException ``` ### CoroutineExceptionHandler 但是如果不想將所有的異常打印在控制臺中呢?[CoroutineExceptionHandler](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html) 上下文元素被用來將通用的 `catch` 代碼塊用于在協程中自定義日志記錄或異常處理。它和使用 [`Thread.uncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)) 很相似。 在 JVM 中可以重定義一個全局的異常處理者來將所有的協程通過[`ServiceLoader`](https://docs.oracle.com/javase/8/docs/api/java/util/ServiceLoader.html) 注冊到 [CoroutineExceptionHandler](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html)。全局異常處理者就如同[`Thread.defaultUncaughtExceptionHandler`](https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)) 一樣,在沒有更多的指定的異常處理者被注冊的時候被使用。在 Android 中, `uncaughtExceptionPreHandler` 被設置在全局協程異常處理者中。 [CoroutineExceptionHandler](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html) 僅在預計不會由用戶處理的異常上調用,所以在 [async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html) 構建器中注冊它沒有任何效果。 ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { //sampleStart val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } val job = GlobalScope.launch(handler) { throw AssertionError() } val deferred = GlobalScope.async(handler) { throw ArithmeticException() // 沒有打印任何東西,依賴用戶去調用 deferred.await() } joinAll(job, deferred) //sampleEnd } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-02.kt)獲取完整代碼。 這段代碼的輸出如下: ```text Caught java.lang.AssertionError ``` ### 取消與異常 取消與異常緊密相關。協程內部使用 `CancellationException` 來進行取消,這個異常會被所有的處理者忽略,所以那些可以被 `catch` 代碼塊捕獲的異常僅僅應該被用來作為額外調試信息的資源。當一個協程在沒有任何理由的情況下使用 [Job.cancel](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html) 取消的時候,它會被終止,但是它不會取消它的父協程。無理由取消是父協程取消其子協程而非取消其自身的機制。 ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { //sampleStart val job = launch { val child = launch { try { delay(Long.MAX_VALUE) } finally { println("Child is cancelled") } } yield() println("Cancelling child") child.cancel() child.join() yield() println("Parent is not cancelled") } job.join() //sampleEnd } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-03.kt)獲取完整代碼。 這段代碼的輸出如下: ```text Cancelling child Child is cancelled Parent is not cancelled ``` 如果協程遇到除 `CancellationException` 以外的異常,它將取消具有該異常的父協程。這種行為不能被覆蓋,且它被用來提供一個穩定的協程層次結構來進行[結構化并發](https://github.com/Kotlin/kotlinx.coroutines/blob/master/docs/composing-suspending-functions.md#structured-concurrency-with-async)而無需依賴[CoroutineExceptionHandler](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html) 的實現。且當所有的子協程被終止的時候,原本的異常被父協程所處理。 > 這也是為什么,在這個例子中,[CoroutineExceptionHandler] 總是被設置在由 [GlobalScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html)啟動的協程中。將異常處理者設置在[runBlocking](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html) 主作用域內啟動的協程中是沒有意義的,盡管子協程已經設置了異常處理者,但是主協程也總是會被取消的。 ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { //sampleStart val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } val job = GlobalScope.launch(handler) { launch { // 第一個子協程 try { delay(Long.MAX_VALUE) } finally { withContext(NonCancellable) { println("Children are cancelled, but exception is not handled until all children terminate") delay(100) println("The first child finished its non cancellable block") } } } launch { // 第二個子協程 delay(10) println("Second child throws an exception") throw ArithmeticException() } } job.join() //sampleEnd } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-04.kt)獲取完整代碼。 這段代碼的輸出如下: ```text Second child throws an exception Children are cancelled, but exception is not handled until all children terminate The first child finished its non cancellable block Caught java.lang.ArithmeticException ``` ### 異常聚合 如果一個協程的多個子協程拋出異常將會發生什么?通常的規則是“第一個異常贏得了勝利”,所以第一個被拋出的異常將會暴露給處理者。但也許這會是異常丟失的原因,比如說一個協程在 `finally` 塊中拋出了一個異常。這時,多余的異常將會被壓制。 > 其中一個解決方法是分別拋出異常,但是接下來 [Deferred.await](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html) 應該有相同的機制來避免行為不一致并且會導致協程的實現細節(是否已將其部分工作委托給子協程)泄漏到異常處理者中。 ```kotlin import kotlinx.coroutines.* import java.io.* fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception with suppressed ${exception.suppressed.contentToString()}") } val job = GlobalScope.launch(handler) { launch { try { delay(Long.MAX_VALUE) } finally { throw ArithmeticException() } } launch { delay(100) throw IOException() } delay(Long.MAX_VALUE) } job.join() } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-05.kt)獲取完整代碼。 > 注意:上面的代碼將只在 JDK7 以上支持 `suppressed` 異常的環境中才能正確工作。 這段代碼的輸出如下: ```text Caught java.io.IOException with suppressed [java.lang.ArithmeticException] ``` > 注意,這個機制當前只能在 Java 1.7 以上的版本中使用。在 JS 和原生環境下暫時會受到限制,但將來會被修復。 取消異常是透明的并且會在默認情況下解包: ```kotlin import kotlinx.coroutines.* import java.io.* fun main() = runBlocking { //sampleStart val handler = CoroutineExceptionHandler { _, exception -> println("Caught original $exception") } val job = GlobalScope.launch(handler) { val inner = launch { launch { launch { throw IOException() } } } try { inner.join() } catch (e: CancellationException) { println("Rethrowing CancellationException with original cause") throw e } } job.join() //sampleEnd } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-exceptions-06.kt)獲取完整代碼。 這段代碼的輸出如下: ```text Rethrowing CancellationException with original cause Caught original java.io.IOException ``` ### 監督 正如我們之前研究的那樣,取消是一種雙向機制,在協程的整個層次結構之間傳播。但是如果需要單向取消怎么辦? 此類需求的一個良好示例是在其作用域內定義作業的 UI 組件。如果任何一個 UI 的子作業執行失敗了,它并不總是有必要取消(有效地殺死)整個 UI 組件,但是如果 UI 組件被銷毀了(并且它的作業也被取消了),由于它的結果不再被需要了,它有必要使所有的子作業執行失敗。 另一個例子是服務進程孵化了一些子作業并且需要 _監督_它們的執行,追蹤它們的故障并在這些子作業執行失敗的時候重啟。 #### 監督作業 [SupervisorJob](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html)可以被用于這些目的。它類似于常規的 [Job](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html),唯一的不同是:SupervisorJob 的取消只會向下傳播。這是非常容易從示例中觀察到的: ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { val supervisor = SupervisorJob() with(CoroutineScope(coroutineContext + supervisor)) { // 啟動第一個子作業——這個示例將會忽略它的異常(不要在實踐中這么做!) val firstChild = launch(CoroutineExceptionHandler { _, _ -> }) { println("First child is failing") throw AssertionError("First child is cancelled") } // 啟動第兩個子作業 val secondChild = launch { firstChild.join() // 取消了第一個子作業且沒有傳播給第二個子作業 println("First child is cancelled: ${firstChild.isCancelled}, but second one is still active") try { delay(Long.MAX_VALUE) } finally { // 但是取消了監督的傳播 println("Second child is cancelled because supervisor is cancelled") } } // 等待直到第一個子作業失敗且執行完成 firstChild.join() println("Cancelling supervisor") supervisor.cancel() secondChild.join() } } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-supervision-01.kt)獲取完整代碼。 這段代碼的輸出如下: ```text First child is failing First child is cancelled: true, but second one is still active Cancelling supervisor Second child is cancelled because supervisor is cancelled ``` #### 監督作用域 對于*作用域*的并發,[supervisorScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/supervisor-scope.html) 可以被用來替代 [coroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html) 來實現相同的目的。它只會單向的傳播并且當子作業自身執行失敗的時候將它們全部取消。它也會在所有的子作業執行結束前等待,就像 [coroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html) 所做的那樣。 ```kotlin import kotlin.coroutines.* import kotlinx.coroutines.* fun main() = runBlocking { try { supervisorScope { val child = launch { try { println("Child is sleeping") delay(Long.MAX_VALUE) } finally { println("Child is cancelled") } } // 使用 yield 來給我們的子作業一個機會來執行打印 yield() println("Throwing exception from scope") throw AssertionError() } } catch(e: AssertionError) { println("Caught assertion error") } } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-supervision-02.kt)獲取完整代碼。 這段代碼的輸出如下: ```text Child is sleeping Throwing exception from scope Child is cancelled Caught assertion error ``` #### 監督協程中的異常 常規的作業和監督作業之間的另一個重要區別是異常處理。每一個子作業應該通過異常處理機制處理自身的異常。 這種差異來自于子作業的執行失敗不會傳播給它的父作業的事實。 ```kotlin import kotlin.coroutines.* import kotlinx.coroutines.* fun main() = runBlocking { val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } supervisorScope { val child = launch(handler) { println("Child throws an exception") throw AssertionError() } println("Scope is completing") } println("Scope is completed") } ``` > 可以在[這里](https://github.com/hltj/kotlinx.coroutines-cn/blob/master/kotlinx-coroutines-core/jvm/test/guide/example-supervision-03.kt)獲取完整代碼。 這段代碼的輸出如下: ```text Scope is completing Child throws an exception Caught java.lang.AssertionError Scope is completed ``` [CancellationException]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-cancellation-exception/index.html [launch]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html [async]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html [Deferred.await]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html [GlobalScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html [CoroutineExceptionHandler]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-exception-handler/index.html [Job.cancel]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/cancel.html [runBlocking]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/run-blocking.html [SupervisorJob()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-supervisor-job.html [Job()]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job.html [supervisorScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/supervisor-scope.html [coroutineScope]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/coroutine-scope.html [actor]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/actor.html [produce]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/produce.html [ReceiveChannel.receive]: https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-receive-channel/receive.html
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看