<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      kotlin協程——>基礎、取消與超時

      Kotlin使用掛起函數為異步操作,使用kotlinx.coroutines中的launch、async

       1. 第?個協程程序

      import kotlinx.coroutines.*
      fun main() {
          GlobalScope.launch { // 在后臺啟動?個新的協程并繼續
              delay(1000L) // ?阻塞的等待 1 秒鐘(默認時間單位是毫秒)
              println("World!") // 在延遲后打印輸出
          }
          println("Hello,") // 協程已在等待時主線程還在繼續
          Thread.sleep(2000L) // 阻塞主線程 2 秒鐘來保證 JVM 存活
      }
      

        代碼運行的結果

      Hello,
      World!
      

        本質上,協程是輕量級的線程。它們在某些 CoroutineScope 上下?中與 launch 協程構建器 ?起啟 動。這?我們在 GlobalScope 中啟動了?個新的協程,這意味著新協程的?命周期只受整個應?程序 的?命周期限制。 可以將 GlobalScope.launch { …… } 替換為 thread { …… } ,并將 delay(……) 替換為 Thread.sleep(……) 達到同樣?的。試試看(不要忘記導? kotlin.concurrent.thread )。 — — — — — — — — — 協程基礎 第?個協程程序 205 如果你?先將 GlobalScope.launch 替換為 thread ,編譯器會報以下錯誤:

      Error: Kotlin: Suspend functions are only allowed to be called from a coroutine or another
      suspend function
      

        這是因為 delay 是?個特殊的 掛起函數 ,它不會造成線程阻塞,但是會 掛起 協程,并且只能在協程中 使?。

      2. 橋接阻塞與?阻塞的世界

        第?個?例在同?段代碼中混?了 ?阻塞的 delay(……) 與 阻塞的 Thread.sleep(……) 。這容易 讓我們記混哪個是阻塞的、哪個是?阻塞的。讓我們顯式使? runBlocking 協程構建器來阻塞:

      import kotlinx.coroutines.*
      fun main() {
          GlobalScope.launch { // 在后臺啟動?個新的協程并繼續
              delay(1000L)
              println("World!")
          }
          println("Hello,") // 主線程中的代碼會?即執?
          runBlocking { // 但是這個表達式阻塞了主線程
              delay(2000L) // ……我們延遲 2 秒來保證 JVM 的存活
          }
      }
      

        結果是相似的,但是這些代碼只使?了?阻塞的函數 delay。調?了 runBlocking 的主線程會?直 阻塞 直到 runBlocking 內部的協程執?完畢。

        這個?例可以使?更合乎慣?法的?式重寫,使? runBlocking 來包裝 main 函數的執?:

      import kotlinx.coroutines.*
      fun main() = runBlocking<Unit> { // 開始執?主協程
          GlobalScope.launch { // 在后臺啟動?個新的協程并繼續
              delay(1000L)
              println("World!")
          }
          println("Hello,") // 主協程在這?會?即執?
          delay(2000L) // 延遲 2 秒來保證 JVM 存活
      }
      

        這?的 runBlocking { …… } 作為?來啟動頂層主協程的適配器。我們顯式指定了其返回 類型 Unit,因為在 Kotlin 中 main 函數必須返回 Unit 類型。

        這也是為掛起函數編寫單元測試的?種?式:

      class MyTest {
          @Test
          fun testMySuspendingFunction() = runBlocking<Unit> {
          // 這?我們可以使?任何喜歡的斷??格來使?掛起函數
          }
      }
      

        延遲?段時間來等待另?個協程運?并不是?個好的選擇。讓我們顯式(以?阻塞?式)等待所啟動的 后臺 Job 執?結束:

      val job = GlobalScope.launch { // 啟動?個新協程并保持對這個作業的引?
          delay(1000L)
          println("World!")
      }
      println("Hello,")
      job.join() // 等待直到?協程執?結束
      

        現在,結果仍然相同,但是主協程與后臺作業的持續時間沒有任何關系了。好多了。

       

      3. 結構化的并發

        協程的實際使?還有?些需要改進的地?。當我們使? GlobalScope.launch 時,我們會創建?個 頂層協程。雖然它很輕量,但它運?時仍會消耗?些內存資源。如果我們忘記保持對新啟動的協程的引 ?,它還會繼續運?。如果協程中的代碼掛起了會怎么樣(例如,我們錯誤地延遲了太?時間),如果我們 啟動了太多的協程并導致內存不?會怎么樣?必須?動保持對所有已啟動協程的引?并 join 之很容易 出錯。 有?個更好的解決辦法。我們可以在代碼中使?結構化并發。我們可以在執?操作所在的指定作?域內 啟動協程,?不是像通常使?線程(線程總是全局的)那樣在 GlobalScope 中啟動。 在我們的?例中,我們使? runBlocking 協程構建器將 main 函數轉換為協程。包括 runBlocking 在內的每個協程構建器都將 CoroutineScope 的實例添加到其代碼塊所在的作?域中。我們可以在這 個作?域中啟動協程??需顯式 join 之,因為外部協程(?例中的 runBlocking )直到在其作?域 中啟動的所有協程都執?完畢后才會結束。因此,可以將我們的?例簡化為:

      import kotlinx.coroutines.*
      fun main() = runBlocking { // this: CoroutineScope
          launch { // 在 runBlocking 作?域中啟動?個新協程
              delay(1000L)
              println("World!")
          }
          println("Hello,")
      }
      

        

      4. 作?域構建器

        除了由不同的構建器提供協程作?域之外,還可以使? coroutineScope 構建器聲明??的作?域。它 會創建?個協程作?域并且在所有已啟動?協程執?完畢之前不會結束。 runBlocking 與 coroutineScope 可能看起來很類似,因為它們都會等待其協程體以及所有?協程結 束。主要區別在于,runBlocking ?法會阻塞當前線程來等待,? coroutineScope 只是掛起,會釋放底 層線程?于其他?途。由于存在這點差異,runBlocking 是常規函數,? coroutineScope 是掛起函數。 可以通過以下?例來演?:

      import kotlinx.coroutines.*
      fun main() = runBlocking { // this: CoroutineScope
          launch {
              delay(200L)
              println("Task from runBlocking")
          }
          coroutineScope { // 創建?個協程作?域
              launch {
                  delay(500L)
                  println("Task from nested launch")
              }
              delay(100L)
              println("Task from coroutine scope") // 這??會在內嵌 launch 之前輸出
          }
          println("Coroutine scope is over") // 這??在內嵌 launch 執?完畢后才輸出
      }
      

        請注意,(當等待內嵌 launch 時)緊挨“Task from coroutine scope”消息之后,就會執?并輸出“Task from runBlocking”?盡管 coroutineScope 尚未結束。

       

      5. 提取函數重構

        我們來將 launch { …… } 內部的代碼塊提取到獨?的函數中。當你對這段代碼執?“提取函數”重構 時,你會得到?個帶有 suspend 修飾符的新函數。這是你的第?個掛起函數。在協程內部可以像普通 函數?樣使?掛起函數,不過其額外特性是,同樣可以使?其他掛起函數(如本例中的 delay )來掛 起協程的執?。

      import kotlinx.coroutines.*
      fun main() = runBlocking {
          launch { doWorld() }
          println("Hello,")
      }
      // 這是你的第?個掛起函數
      suspend fun doWorld() {
          delay(1000L)
          println("World!")
      }
      

        但是如果提取出的函數包含?個在當前作?域中調?的協程構建器的話,該怎么辦?在這種情況下,所 提取函數上只有 suspend 修飾符是不夠的。為 CoroutineScope 寫?個 doWorld 擴展?法是其 中?種解決?案,但這可能并?總是適?,因為它并沒有使 API 更加清晰。慣?的解決?案是要么顯式 將 CoroutineScope 作為包含該函數的類的?個字段,要么當外部類實現了 CoroutineScope 時 隱式取得。作為最后的?段,可以使? CoroutineScope(coroutineContext),不過這種?法結構上不安 全,因為你不能再控制該?法執?的作?域。只有私有 API 才能使?這個構建器。

       

      6.全局協程像守護線程

        以下代碼在 GlobalScope 中啟動了?個?期運?的協程,該協程每秒輸出“I'm sleeping”兩次,之后在 主函數中延遲?段時間后返回。

      GlobalScope.launch {
          repeat(1000) { i ->
              println("I'm sleeping $i ...")
              delay(500L)
          }
      }
      delay(1300L) // 在延遲后退出
      

        你可以運?這個程序并看到它輸出了以下三?后終?:

      I'm sleeping 0 ...
      I'm sleeping 1 ...
      I'm sleeping 2 ...
      

        在 GlobalScope 中啟動的活動協程并不會使進程保活。它們就像守護線程

       

      7.取消協程的執行

        在?個?時間運?的應?程序中,你也許需要對你的后臺協程進?細粒度的控制。?如說,?個??也 許關閉了?個啟動了協程的界?,那么現在協程的執?結果已經不再被需要了,這時,它應該是可以被 取消的。該 launch 函數返回了?個可以被?來取消運?中的協程的 Job:

      val job = launch {
          repeat(1000) { i ->
              println("job: I'm sleeping $i ...")
              delay(500L)
          }
      }
      delay(1300L) // 延遲?段時間
      println("main: I'm tired of waiting!")
      job.cancel() // 取消該作業
      job.join() // 等待作業執?結束
      println("main: Now I can quit.")
      

        程序執?后的輸出如下:

      job: I'm sleeping 0 ...
      job: I'm sleeping 1 ...
      job: I'm sleeping 2 ...
      main: I'm tired of waiting!
      main: Now I can quit.
      

        ?旦 main 函數調?了 job.cancel ,我們在其它的協程中就看不到任何輸出,因為它被取消了。這? 也有?個可以使 Job 掛起的函數 cancelAndJoin 它合并了對 cancel 以及 join 的調?。

      8.取消是協作的

        協程的取消是 協作 的。?段協程代碼必須協作才能被取消。所有 kotlinx.coroutines 中的掛起 函數都是 可被取消的 。它們檢查協程的取消,并在取消時拋出 CancellationException。然?,如果協 程正在執?計算任務,并且沒有檢查取消的話,那么它是不能被取消的,就如如下?例代碼所?:

      val startTime = System.currentTimeMillis()
      val job = launch(Dispatchers.Default) {
          var nextPrintTime = startTime
          var i = 0
          while (i < 5) { // ?個執?計算的循環,只是為了占? CPU
      // 每秒打印消息兩次
              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.")
      

        運??例代碼,并且我們可以看到它連續打印出了“I'm sleeping”,甚?在調?取消后,作業仍然執?了 五次循環迭代并運?到了它結束為?。

       

      9.使計算代碼可取消

        我們有兩種?法來使執?計算的代碼可以被取消。第?種?法是定期調?掛起函數來檢查取消。對于這 種?的 yield 是?個好的選擇。另?種?法是顯式的檢查取消狀態。讓我們試試第?種?法。 將前?個?例中的 while (i < 5) 替換為 while (isActive) 并重新運?它。

      val startTime = System.currentTimeMillis()
      val job = launch(Dispatchers.Default) {
          var nextPrintTime = startTime
          var i = 0
          while (isActive) { // 可以被取消的計算循環
      // 每秒打印消息兩次
              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 是?個可以被使?在 CoroutineScope 中的擴展屬性。

       

      10. 在 finally 中釋放資源

        我們通常使?如下的?法處理在被取消時拋出 CancellationException 的可被取消的掛起函數。?如 說,try {……} finally {……} 表達式以及 Kotlin 的 use 函數?般在協程被取消的時候執?它們 的終結動作:

      val job = launch {
          try {
              repeat(1000) { i ->
                  println("job: I'm sleeping $i ...")
                  delay(500L)
              }
          } finally {
              println("job: I'm running finally")
          }
      }
      delay(1300L) // 延遲?段時間
      println("main: I'm tired of waiting!")
      job.cancelAndJoin() // 取消該作業并且等待它結束
      println("main: Now I can quit.")
      

        join 和 cancelAndJoin 等待了所有的終結動作執?完畢,所以運??例得到了下?的輸出:

      job: I'm sleeping 0 ...
      job: I'm sleeping 1 ...
      job: I'm sleeping 2 ...
      main: I'm tired of waiting!
      job: I'm running finally
      main: Now I can quit.
      

        

      11.  運?不能取消的代碼塊

        在前?個例?中任何嘗試在 finally 塊中調?掛起函數的?為都會拋出 CancellationException,因 為這?持續運?的代碼是可以被取消的。通常,這并不是?個問題,所有良好的關閉操作(關閉?個? 件、取消?個作業、或是關閉任何?種通信通道)通常都是?阻塞的,并且不會調?任何掛起函數。然?, 在真實的案例中,當你需要掛起?個被取消的協程,你可以將相應的代碼包裝在 withContext(NonCancellable) {……} 中,并使? withContext 函數以及 NonCancellable 上 下?,?如下?例所?:

      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)
                  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.")
      

        

      12. 超時

        在實踐中絕?多數取消?個協程的理由是它有可能超時。當你?動追蹤?個相關 Job 的引?并啟動了 ?個單獨的協程在延遲后取消追蹤,這?已經準備好使? withTimeout 函數來做這件事。來看看?例代碼:

      withTimeout(1300L) {
          repeat(1000) { i ->
              println("I'm sleeping $i ...")
              delay(500L)
          }
      }
      

        運?后得到如下輸出:

      I'm sleeping 0 ...
      I'm sleeping 1 ...
      I'm sleeping 2 ...
      Exception in thread "main" kotlinx.coroutines.TimeoutCancellationException: Timed out
      waiting for 1300 ms
      

        withTimeout 拋出了 TimeoutCancellationException ,它是 CancellationException 的?類。 我們之前沒有在控制臺上看到堆棧跟蹤信息的打印。這是因為在被取消的協程中 CancellationException 被認為是協程執?結束的正常原因。然?,在這個?例中我們在 main 函數中正確地使?了 withTimeout 

        由于取消只是?個例外,所有的資源都使?常?的?法來關閉。如果你需要做?些各類使?超時的特別 的額外操作,可以使?類似 withTimeout 的 withTimeoutOrNull 函數,并把這些會超時的代碼包裝在 try {...} catch (e: TimeoutCancellationException) {...} 代碼塊中,? withTimeoutOrNull 通過返回 null 來進?超時操作,從?替代拋出?個異常:

      val result = withTimeoutOrNull(1300L) {
          repeat(1000) { i ->
              println("I'm sleeping $i ...")
              delay(500L)
          }
          "Done" // 在它運?得到結果之前取消它
      }
      println("Result is $result")
      

        運?這段代碼時不再拋出異常:

      I'm sleeping 0 ...
      I'm sleeping 1 ...
      I'm sleeping 2 ...
      Result is null
      

        

       

      posted @ 2021-02-22 13:50  王世楨  閱讀(1172)  評論(0)    收藏  舉報
      主站蜘蛛池模板: av色蜜桃一区二区三区| 午夜免费无码福利视频麻豆| а∨天堂一区中文字幕| 国产亚洲一二三区精品| 久久人人妻人人爽人人爽| 一区二区三区国产亚洲网站| 天堂av网一区二区三区| 日韩精品成人网页视频在线 | 精品无码国产日韩制服丝袜| 亚洲黄色第一页在线观看| 国产日韩综合av在线| 国产稚嫩高中生呻吟激情在线视频| Y111111国产精品久久久| 精品午夜福利无人区乱码| 国产亚洲精品久久yy50| 国产一区二区不卡在线| 熟妇人妻中文a∨无码| 成人无码精品免费视频在线观看| 亚洲日韩久热中文字幕| 精品无码国产自产拍在线观看蜜| 绍兴县| 视频免费完整版在线播放| 日韩av综合免费在线| 一二三四日本高清社区5| 九九热在线视频免费播放| 国产精品av中文字幕| 久久精品人人做人人爽电影蜜月| 国产成人精品亚洲资源| 无套内谢少妇一二三四| 国产成人精品一区二三区在线观看| 日韩一区二区三区水蜜桃| 国产成人精彩在线视频| 万山特区| 成人嫩草研究院久久久精品| 国产又黄又硬又粗| 久久精品不卡一区二区| 亚洲精品岛国片在线观看| 乱人伦中文字幕成人网站在线| 亚洲色一色噜一噜噜噜| 一区二区三区午夜无码视频| V一区无码内射国产|