在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Android/ Kotlin協(xié)程
Kotlin內(nèi)聯(lián)函數(shù)
Kotlin開(kāi)發(fā)環(huán)境設(shè)置(Eclipse)
Kotlin調(diào)用Java代碼
Kotlin使用Ant
Kotlin編譯器插件
Kotlin相等性
Kotlin JavaScript模塊
編寫(xiě)Kotlin代碼文檔
Kotlin返回和跳轉(zhuǎn)
Kotlin異常處理
Kotlin可見(jiàn)性修飾符
Kotlin委托
Kotlin委托屬性
Kotlin編碼約定/編碼風(fēng)格
Kotlin基礎(chǔ)語(yǔ)法
使用Kotlin進(jìn)行服務(wù)器端開(kāi)發(fā)
Kotlin接口
Kotlin反射
Kotlin類型別名
Kotlin枚舉類
Kotlin當(dāng)前版本是多少?
Kotlin注解處理工具
Kotlin類型的檢查與轉(zhuǎn)換
Kotlin屬性和字段
Kotlin類型安全的構(gòu)建器
Kotlin相比Java語(yǔ)言有哪些優(yōu)點(diǎn)?
Kotlin JavaScript反射
Kotlin 是什么?
Kotlin泛型
Kotlin慣用語(yǔ)法
Kotlin與OSGi
Kotlin數(shù)據(jù)類型
Kotlin是面向?qū)ο筮€是函數(shù)式語(yǔ)言?
Kotlin動(dòng)態(tài)類型
Kotlin協(xié)程
Kotlin操作符符重載
Kotlin使用Gradle
Kotlin密封類
Kotlin兼容性
Kotlin集合
Kotlin調(diào)用JavaScript
Kotlin null值安全
Kotlin函數(shù)
Kotlin開(kāi)發(fā)環(huán)境設(shè)置(IntelliJ IDEA)
Kotlin嵌套類
Kotlin控制流程
Kotlin和Java語(yǔ)言比較
Kotlin 與 Java 語(yǔ)言兼容嗎?
Kotlin教程
Kotlin類和繼承
Kotlin對(duì)象表達(dá)式和對(duì)象聲明
JavaScript中調(diào)用Kotlin
Kotlin區(qū)間/范圍
Kotlin數(shù)據(jù)類
Kotlin lambda表達(dá)式
Kotlin是免費(fèi)的嗎?
Kotlin包
使用Kotlin進(jìn)行Android開(kāi)發(fā)
在Java中調(diào)用Kotlin代碼
Kotlin this表達(dá)式
使用Kotlin進(jìn)行JavaScript開(kāi)發(fā)
Kotlin擴(kuò)展
Kotlin解構(gòu)聲明
Kotlin注解
Kotlin使用Maven

Kotlin協(xié)程

一些 API 啟動(dòng)長(zhǎng)時(shí)間運(yùn)行的操作(例如網(wǎng)絡(luò) IO、文件 IO、CPU 或 GPU 密集型任務(wù)等),并要求調(diào)用者阻塞直到它們完成。協(xié)程提供了一種避免阻塞線程并用更廉價(jià)、更可控的操作替代線程阻塞的方法:協(xié)程掛起。

在 Kotlin 1.1 中協(xié)程是實(shí)驗(yàn)性的。詳見(jiàn)下文

協(xié)程通過(guò)將復(fù)雜性放入庫(kù)來(lái)簡(jiǎn)化異步編程。程序的邏輯可以在協(xié)程中順序地表達(dá),而底層庫(kù)會(huì)為我們解決其異步性。該庫(kù)可以將用戶代碼的相關(guān)部分包裝為回調(diào)、訂閱相關(guān)事件、在不同線程(甚至不同機(jī)器!)上調(diào)度執(zhí)行,而代碼則保持如同順序執(zhí)行一樣簡(jiǎn)單。

許多在其他語(yǔ)言中可用的異步機(jī)制可以使用 Kotlin 協(xié)程實(shí)現(xiàn)為庫(kù)。這包括源于 C# 和 ECMAScript 的 async/await、源于 Go 的 管道select 以及源于 C# 和 Python 生成器/yield。關(guān)于提供這些結(jié)構(gòu)的庫(kù)請(qǐng)參見(jiàn)其下文描述。

阻塞 vs 掛起

基本上,協(xié)程計(jì)算可以被掛起而無(wú)需阻塞線程。線程阻塞的代價(jià)通常是昂貴的,尤其在高負(fù)載時(shí),因?yàn)橹挥邢鄬?duì)少量線程實(shí)際可用,因此阻塞其中一個(gè)會(huì)導(dǎo)致一些重要的任務(wù)被延遲。

另一方面,協(xié)程掛起幾乎是無(wú)代價(jià)的。不需要上下文切換或者 OS 的任何其他干預(yù)。最重要的是,掛起可以在很大程度上由用戶庫(kù)控制:作為庫(kù)的作者,我們可以決定掛起時(shí)發(fā)生什么并根據(jù)需求優(yōu)化/記日志/截獲。

另一個(gè)區(qū)別是,協(xié)程不能在隨機(jī)的指令中掛起,而只能在所謂的掛起點(diǎn)掛起,這會(huì)調(diào)用特別標(biāo)記的函數(shù)。

掛起函數(shù)

當(dāng)我們調(diào)用標(biāo)記有特殊修飾符 suspend 的函數(shù)時(shí),會(huì)發(fā)生掛起:

suspend fun doSomething(foo: Foo): Bar {
    ……
}

這樣的函數(shù)稱為掛起函數(shù),因?yàn)檎{(diào)用它們可能掛起協(xié)程(如果相關(guān)調(diào)用的結(jié)果已經(jīng)可用,庫(kù)可以決定繼續(xù)進(jìn)行而不掛起)。掛起函數(shù)能夠以與普通函數(shù)相同的方式獲取參數(shù)和返回值,但它們只能從協(xié)程和其他掛起函數(shù)中調(diào)用。事實(shí)上,要啟動(dòng)協(xié)程,必須至少有一個(gè)掛起函數(shù),它通常是匿名的(即它是一個(gè)掛起 lambda 表達(dá)式)。讓我們來(lái)看一個(gè)例子,一個(gè)簡(jiǎn)化的 async() 函數(shù)(源自 kotlinx.coroutines 庫(kù)):

fun <T> async(block: suspend () -> T)

這里的 async() 是一個(gè)普通函數(shù)(不是掛起函數(shù)),但是它的 block 參數(shù)具有一個(gè)帶 suspend 修飾符的函數(shù)類型: suspend () -> T。所以,當(dāng)我們將一個(gè) lambda 表達(dá)式傳給 async() 時(shí),它會(huì)是掛起 lambda 表達(dá)式,于是我們可以從中調(diào)用掛起函數(shù):

async {
    doSomething(foo)
    ……
}

繼續(xù)該類比,await() 可以是一個(gè)掛起函數(shù)(因此也可以在一個(gè) async {} 塊中調(diào)用),該函數(shù)掛起一個(gè)協(xié)程,直到一些計(jì)算完成并返回其結(jié)果:

async {
    ……
    val result = computation.await()
    ……
}

更多關(guān)于 async/await 函數(shù)實(shí)際在 kotlinx.coroutines 中如何工作的信息可以在這里找到。

請(qǐng)注意,掛起函數(shù) await()doSomething() 不能在像 main() 這樣的普通函數(shù)中調(diào)用:

fun main(args: Array<String>) {
    doSomething() // 錯(cuò)誤:掛起函數(shù)從非協(xié)程上下文調(diào)用
}

還要注意的是,掛起函數(shù)可以是虛擬的,當(dāng)覆蓋它們時(shí),必須指定 suspend 修飾符:

interface Base {
    suspend fun foo()
}

class Derived: Base {
    override suspend fun foo() { …… }
}

@RestrictsSuspension 注解

擴(kuò)展函數(shù)(和 lambda 表達(dá)式)也可以標(biāo)記為 suspend,就像普通的一樣。這允許創(chuàng)建 DSL 及其他用戶可擴(kuò)展的 API。在某些情況下,庫(kù)作者需要阻止用戶添加新方式來(lái)掛起協(xié)程。

為了實(shí)現(xiàn)這一點(diǎn),可以使用 @RestrictsSuspension 注解。當(dāng)接收者類/接口 R 用它標(biāo)注時(shí),所有掛起擴(kuò)展都需要委托給 R 的成員或其它委托給它的擴(kuò)展。由于擴(kuò)展不能無(wú)限相互委托(程序不會(huì)終止),這保證所有掛起都通過(guò)調(diào)用 R 的成員發(fā)生,庫(kù)的作者就可以完全控制了。

這在少數(shù)情況是需要的,當(dāng)每次掛起在庫(kù)中以特殊方式處理時(shí)。例如,當(dāng)通過(guò) buildSequence() 函數(shù)實(shí)現(xiàn)下文所述的生成器時(shí),我們需要確保在協(xié)程中的任何掛起調(diào)用最終調(diào)用 yield()yieldAll() 而不是任何其他函數(shù)。這就是為什么 SequenceBuilder@RestrictsSuspension 注解:

@RestrictsSuspension
public abstract class SequenceBuilder<in T> {
    ……
}

參見(jiàn)其 Github 上 的源代碼。

協(xié)程的內(nèi)部機(jī)制

我們不是在這里給出一個(gè)關(guān)于協(xié)程如何工作的完整解釋,然而粗略地認(rèn)識(shí)發(fā)生了什么是相當(dāng)重要的。

協(xié)程完全通過(guò)編譯技術(shù)實(shí)現(xiàn)(不需要來(lái)自 VM 或 OS 端的支持),掛起通過(guò)代碼來(lái)生效?;旧?,每個(gè)掛起函數(shù)(優(yōu)化可能適用,但我們不在這里討論)都轉(zhuǎn)換為狀態(tài)機(jī),其中的狀態(tài)對(duì)應(yīng)于掛起調(diào)用。剛好在掛起前,下一狀態(tài)與相關(guān)局部變量等一起存儲(chǔ)在編譯器生成的類的字段中。在恢復(fù)該協(xié)程時(shí),恢復(fù)局部變量并且狀態(tài)機(jī)從剛好掛起之后的狀態(tài)進(jìn)行。

掛起的協(xié)程可以作為保持其掛起狀態(tài)與局部變量的對(duì)象來(lái)存儲(chǔ)和傳遞。這種對(duì)象的類型是 Continuation,而這里描述的整個(gè)代碼轉(zhuǎn)換對(duì)應(yīng)于經(jīng)典的延續(xù)性傳遞風(fēng)格(Continuation-passing style)。因此,掛起函數(shù)有一個(gè) Continuation 類型的額外參數(shù)作為高級(jí)選項(xiàng)。

關(guān)于協(xié)程工作原理的更多細(xì)節(jié)可以在這個(gè)設(shè)計(jì)文檔中找到。在其他語(yǔ)言(如 C# 或者 ECMAScript 2016)中的 async/await 的類似描述與此相關(guān),雖然它們實(shí)現(xiàn)的語(yǔ)言功能可能不像 Kotlin 協(xié)程這樣通用。

協(xié)程的實(shí)驗(yàn)性狀態(tài)

協(xié)程的設(shè)計(jì)是實(shí)驗(yàn)性的,這意味著它可能在即將發(fā)布的版本中更改。當(dāng)在 Kotlin 1.1 中編譯協(xié)程時(shí),默認(rèn)情況下會(huì)報(bào)一個(gè)警告:“協(xié)程”功能是實(shí)驗(yàn)性的。要移出該警告,你需要指定 opt-in 標(biāo)志

由于其實(shí)驗(yàn)性狀態(tài),標(biāo)準(zhǔn)庫(kù)中協(xié)程相關(guān)的 API 放在 kotlin.coroutines.experimental 包下。當(dāng)設(shè)計(jì)完成并且實(shí)驗(yàn)性狀態(tài)解除時(shí),最終的 API 會(huì)移動(dòng)到 kotlin.coroutines,并且實(shí)驗(yàn)包會(huì)被保留(可能在一個(gè)單獨(dú)的構(gòu)件中)以實(shí)現(xiàn)向后兼容。

重要注意事項(xiàng):我們建議庫(kù)作者遵循相同慣例:給暴露基于協(xié)程 API 的包添加“experimental”后綴(如 com.example.experimental),以使你的庫(kù)保持二進(jìn)制兼容。當(dāng)最終 API 發(fā)布時(shí),請(qǐng)按照下列步驟操作:

  • 將所有 API 復(fù)制到 com.example(沒(méi)有 experimental 后綴),
  • 保持實(shí)驗(yàn)包的向后兼容性。

這將最小化你的用戶的遷移問(wèn)題。

標(biāo)準(zhǔn) API

協(xié)程有三個(gè)主要組成部分:

  • 語(yǔ)言支持(即如上所述的掛起功能),
  • Kotlin 標(biāo)準(zhǔn)庫(kù)中的底層核心 API,
  • 可以直接在用戶代碼中使用的高級(jí) API。

底層 API:kotlin.coroutines

底層 API 相對(duì)較小,并且除了創(chuàng)建更高級(jí)的庫(kù)之外,不應(yīng)該使用它。 它由兩個(gè)主要包組成:

關(guān)于這些 API 用法的更多細(xì)節(jié)可以在這里找到。

kotlin.coroutines 中的生成器 API

kotlin.coroutines.experimental 中僅有的“應(yīng)用程序級(jí)”函數(shù)是

這些包含在 kotlin-stdlib 中因?yàn)樗麄兣c序列相關(guān)。這些函數(shù)(我們可以僅限于這里的 buildSequence())實(shí)現(xiàn)了 生成器 ,即提供一種廉價(jià)構(gòu)建惰性序列的方法:



kotlin import kotlin.coroutines.experimental.* fun main(args: Array<String>) { //sampleStart val fibonacciSeq = buildSequence { var a = 0 var b = 1 yield(1) while (true) { yield(a + b) val tmp = a + b a = b b = tmp } } //sampleEnd // 輸出前五個(gè)斐波納契數(shù)字 println(fibonacciSeq.take(8).toList()) }

這通過(guò)創(chuàng)建一個(gè)協(xié)程生成一個(gè)惰性的、潛在無(wú)限的斐波那契數(shù)列,該協(xié)程通過(guò)調(diào)用 yield() 函數(shù)來(lái)產(chǎn)生連續(xù)的斐波納契數(shù)。當(dāng)在這樣的序列的迭代器上迭代每一步,都會(huì)執(zhí)行生成下一個(gè)數(shù)的協(xié)程的另一部分。因此,我們可以從該序列中取出任何有限的數(shù)字列表,例如 fibonacciSeq.take(8).toList() 結(jié)果是 [1, 1, 2, 3, 5, 8, 13, 21]。協(xié)程足夠廉價(jià)使這很實(shí)用。

為了演示這樣一個(gè)序列的真正惰性,讓我們?cè)谡{(diào)用 buildSequence() 內(nèi)部輸出一些調(diào)試信息:



kotlin import kotlin.coroutines.experimental.* fun main(args: Array<String>) { //sampleStart val lazySeq = buildSequence { print("START ") for (i in 1..5) { yield(i) print("STEP ") } print("END") } // 輸出序列的前三個(gè)元素 lazySeq.take(3).forEach { print("$it ") } //sampleEnd }

運(yùn)行上面的代碼看,是不是我們輸出前三個(gè)元素的數(shù)字與生成循環(huán)的 STEP 有交叉。這意味著計(jì)算確實(shí)是惰性的。要輸出 1,我們只執(zhí)行到第一個(gè) yield(i),并且過(guò)程中會(huì)輸出 START。然后,輸出 2,我們需要繼續(xù)下一個(gè) yield(i),并會(huì)輸出 STEP。3 也一樣。永遠(yuǎn)不會(huì)輸出再下一個(gè) STEP(以及END),因?yàn)槲覀冊(cè)僖矝](méi)有請(qǐng)求序列的后續(xù)元素。

為了一次產(chǎn)生值的集合(或序列),可以使用 yieldAll() 函數(shù):



kotlin import kotlin.coroutines.experimental.* fun main(args: Array<String>) { //sampleStart val lazySeq = buildSequence { yield(0) yieldAll(1..10) } lazySeq.forEach { print("$it ") } //sampleEnd }

buildIterator() 的工作方式類似于 buildSequence(),但返回一個(gè)惰性迭代器。

可以通過(guò)為 SequenceBuilder 類寫(xiě)掛起擴(kuò)展(帶有上文描述的 @RestrictsSuspension 注解)來(lái)為 buildSequence() 添加自定義生產(chǎn)邏輯(custom yielding logic):



kotlin import kotlin.coroutines.experimental.* //sampleStart suspend fun SequenceBuilder<Int>.yieldIfOdd(x: Int) { if (x % 2 != 0) yield(x) } val lazySeq = buildSequence { for (i in 1..10) yieldIfOdd(i) } //sampleEnd fun main(args: Array<String>) { lazySeq.forEach { print("$it ") } }

其他高級(jí) API:kotlinx.coroutines

只有與協(xié)程相關(guān)的核心 API 可以從 Kotlin 標(biāo)準(zhǔn)庫(kù)獲得。這主要包括所有基于協(xié)程的庫(kù)可能使用的核心原語(yǔ)和接口。

大多數(shù)基于協(xié)程的應(yīng)用程序級(jí)API都作為單獨(dú)的庫(kù)發(fā)布:kotlinx.coroutines。這個(gè)庫(kù)覆蓋了

  • 使用 kotlinx-coroutines-core 的平臺(tái)無(wú)關(guān)異步編程
    • 此模塊包括支持 select 和其他便利原語(yǔ)的類似 Go 的管道
    • 這個(gè)庫(kù)的綜合指南在這里。
  • 基于 JDK 8 中的 CompletableFuture 的 API:kotlinx-coroutines-jdk8
  • 基于 JDK 7 及更高版本 API 的非阻塞 IO(NIO):kotlinx-coroutines-nio
  • 支持 Swing (kotlinx-coroutines-swing) 和 JavaFx (kotlinx-coroutines-javafx)
  • 支持 RxJava:kotlinx-coroutines-rx

這些庫(kù)既作為使通用任務(wù)易用的便利的 API,也作為如何構(gòu)建基于協(xié)程的庫(kù)的端到端示例。


上一篇:Kotlin null值安全下一篇:Kotlin接口