Android Kotlin 协程async
轉載請標明出處:http://blog.csdn.net/zhaoyanjun6/article/details/118093646
本文出自【趙彥軍的博客】
往期精彩文章
Android Coroutines Channels
Kotlin實戰指南二十:flow
Kotlin實戰指南十六:Synchronized、Volatile
文章目錄
- measureTimeMillis 統計一段代碼耗時
- 使用默認順序
- 使用 async 并發
- 惰性啟動的 async
- 構建async 風格的函數
- 使用 async 的結構化并發
measureTimeMillis 統計一段代碼耗時
內斂函數 measureTimeMillis{ } 可以很方便的統計一段代碼執行的耗時。
使用:
輸出結果:
D/zyj-: 日志 D/zyj-: 耗時:1010使用默認順序
定義兩個耗時函數:
suspend fun doSomethingUsefulOne(): Int {delay(1000L) // 假設我們在這里做了一些有用的事return 13 }suspend fun doSomethingUsefulTwo(): Int {delay(1000L) // 假設我們在這里也做了一些有用的事return 29 }使用默認的順序調用:
val time = measureTimeMillis {val one = doSomethingUsefulOne()val two = doSomethingUsefulTwo()println("The answer is ${one + two}") } println("Completed in $time ms")它的打印輸出如下:
The answer is 42 Completed in 2017 ms從輸出結果上看,兩個耗時任務是串行的,總耗時= 耗時函數1 + 耗時函數2
使用 async 并發
如果 doSomethingUsefulOne 與 doSomethingUsefulTwo 之間沒有依賴,并且我們想更快的得到結果,讓它們進行 并發 嗎?這就是async 可以幫助我們的地方。
在概念上,async 就類似于 launch。它啟動了一個單獨的協程與其它所有的協程一起并發的工作。不同之處在于 launch 返回一個 Job 并且不附帶任何結果值,而 async 返回一個 Deferred—— 一個非阻塞 future, 這代表了一個將會在稍后提供結果的 promise。你可以使用 .await()在一個延期的值上得到它的最終結果, 但是 Deferred 也是一個 Job,所以如果需要的話,你可以取消它。
val time = measureTimeMillis {val one = async { doSomethingUsefulOne() }val two = async { doSomethingUsefulTwo() }println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms")它的打印輸出如下:
The answer is 42 Completed in 1017 ms這里快了兩倍,因為兩個協程并發執行。 請注意,使用協程進行并發總是顯式的。
惰性啟動的 async
可選的,async 可以通過將 start 參數設置為 CoroutineStart.LAZY 而變為惰性的。 在這個模式下,只有結果通過 await 獲取的時候協程才會啟動,或者在 Job 的 start函數調用的時候。運行下面的示例:
val time = measureTimeMillis {val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }// 執行一些計算one.start() // 啟動第一個two.start() // 啟動第二個println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms")它的打印輸出如下:
The answer is 42 Completed in 1017 ms因此,在先前的例子中這里定義的兩個協程沒有執行,但是控制權在于程序員準確的在開始執行時調用 start。我們首先 調用 one,然后調用 two,接下來等待這個協程執行完畢。
注意,如果我們只是在 println 中調用 await,而沒有在單獨的協程中調用 start,這將會導致順序行為,直到 await 啟動該協程 執行并等待至它結束,這并不是惰性的預期用例。 在計算一個值涉及掛起函數時,這個 async(start = CoroutineStart.LAZY)的用例用于替代標準庫中的 lazy 函數。
構建async 風格的函數
我們可以定義異步風格的函數來 異步 的調用 doSomethingUsefulOne 和 doSomethingUsefulTwo 并使用 async 協程建造器并帶有一個顯式的 GlobalScope 引用。 我們給這樣的函數的名稱中加上“……Async”后綴來突出表明:事實上,它們只做異步計算并且需要使用延期的值來獲得結果。
// somethingUsefulOneAsync 函數的返回值類型是 Deferred<Int> fun somethingUsefulOneAsync() = GlobalScope.async {doSomethingUsefulOne() }// somethingUsefulTwoAsync 函數的返回值類型是 Deferred<Int> fun somethingUsefulTwoAsync() = GlobalScope.async {doSomethingUsefulTwo() }使用
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)// 我們可以在協程外面啟動異步執行val one = somethingUsefulOneAsync()val two = somethingUsefulTwoAsync()// 但是等待結果必須調用其它的掛起或者阻塞// 當我們等待結果的時候,這里我們使用 `GlobalScope.launch{ }` 來阻塞主線程GlobalScope.launch {println("The- answer is ${one.await()} ${two.await()}")}} }這種帶有異步函數的編程風格僅供參考,因為這在其它編程語言中是一種受歡迎的風格。在 Kotlin 的協程中使用這種風格是強烈不推薦的, 原因如下所述。
考慮一下如果 val one = somethingUsefulOneAsync() 這一行和 one.await() 表達式這里在代碼中有邏輯錯誤, 并且程序拋出了異常以及程序在操作的過程中中止,將會發生什么。 通常情況下,一個全局的異常處理者會捕獲這個異常,將異常打印成日記并報告給開發者,但是反之該程序將會繼續執行其它操作。但是這里我們的 somethingUsefulOneAsync仍然在后臺執行, 盡管如此,啟動它的那次操作也會被終止。這個程序將不會進行結構化并發,如下一小節所示。
使用 async 的結構化并發
讓我們使用使用 async 的并發這一小節的例子并且提取出一個函數并發的調用 doSomethingUsefulOne 與 doSomethingUsefulTwo 并且返回它們兩個的結果之和。 由于 async 被定義為了 CoroutineScope上的擴展,我們需要將它寫在作用域內,并且這是 coroutineScope 函數所提供的:
suspend fun concurrentSum(): Int = coroutineScope {val one = async { doSomethingUsefulOne() }val two = async { doSomethingUsefulTwo() }one.await() + two.await() }這種情況下,如果在 concurrentSum 函數內部發生了錯誤,并且它拋出了一個異常, 所有在作用域中啟動的協程都會被取消。
val time = measureTimeMillis {println("The answer is ${concurrentSum()}") } println("Completed in $time ms")從上面的 main 函數的輸出可以看出,我們仍然可以同時執行這兩個操作:
The answer is 42 Completed in 1017 ms取消始終通過協程的層次結構來進行傳遞:
import kotlinx.coroutines.*fun main() = runBlocking<Unit> {try {failedConcurrentSum()} catch(e: ArithmeticException) {println("Computation failed with ArithmeticException")} }suspend fun failedConcurrentSum(): Int = coroutineScope {val one = async<Int> { try {delay(Long.MAX_VALUE) // 模擬一個長時間的運算42} finally {println("First child was cancelled")}}val two = async<Int> { println("Second child throws an exception")throw ArithmeticException()}one.await() + two.await() }請注意,如果其中一個子協程(即 two)失敗,第一個 async 以及等待中的父協程都會被取消:
Second child throws an exception First child was cancelled Computation failed with ArithmeticException 與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Android Kotlin 协程async的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Kotlin协程和Ret
- 下一篇: Android Kotlin Excep