提示:此文章僅作為本人記錄日常學(xué)習(xí)使用,若有存在錯誤或者不嚴(yán)謹(jǐn)?shù)玫胤綒g迎指正。
一、Kotlin中的協(xié)程
協(xié)程是Kotlin語言中很有代表性的一種并發(fā)設(shè)計模式,用于簡化異步執(zhí)行的代碼。協(xié)程和線程有點(diǎn)類似,可以簡單地將它理解成一種輕量級的線程。我們前面學(xué)習(xí)的線程是屬于重量級的,這是因?yàn)?strong>線程需要依靠操作系統(tǒng)的調(diào)度來實(shí)現(xiàn)不同線程之間的切換。而協(xié)程僅在編程語言的層面就能實(shí)現(xiàn)不同協(xié)程之間的切換,無需操作系統(tǒng)的介入,從而極大提高了并發(fā)編程的運(yùn)行效率。
舉一個具體的例子,例如我們有foo()和bar()這兩個方法:
fun foo(){
a()
b()
c()
}
fun bar(){
x()
y()
z()
}
在沒有開啟線程的情況下,先調(diào)用foo()方法后調(diào)用bar()方法,理論上結(jié)果一定是a()、b()、c()執(zhí)行完了以后,x()、y()、z()才能夠得到執(zhí)行。而如果在協(xié)程A中調(diào)用foo()方法,在協(xié)程B中調(diào)用bar()方法。雖然它們?nèi)赃\(yùn)行在同一個線程中,但在執(zhí)行foo()方法時隨時都有可能被掛起而去執(zhí)行bar()方法;同理在執(zhí)行bar()方法時也隨時都有可能被掛起轉(zhuǎn)而繼續(xù)執(zhí)行foo()方法,這就使得最終輸出的結(jié)果變得不確定了。
可以看出,協(xié)程允許我們在單線程模式下模擬多線程編程的效果,代碼執(zhí)行時的掛起與恢復(fù)完全是由編程語言來控制的,和操作系統(tǒng)無關(guān)。
1.1 協(xié)程的基本用法
如果我們需要在項(xiàng)目中使用協(xié)程功能,需要在build.gradle.kts(:app)中添加以下依賴:
dependencies {
· · ·
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9") // 適用于Android項(xiàng)目
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") // 適用于非Android項(xiàng)目
}
創(chuàng)建一個CoroutinesTest.kt文件并在其中定義一個main()函數(shù),然后在main()函數(shù)中使用GlobalScope.launch函數(shù):
fun main() {
GlobalScope.launch {
println("codes run in coroutine scope")
}
}
GlobalScope.launch函數(shù)可以創(chuàng)建一個協(xié)程的作用域,這樣代碼塊中的代碼就是在協(xié)程中運(yùn)行的了。按照我們的理解,現(xiàn)在運(yùn)行main()函數(shù),應(yīng)該會打印一句話才對??墒钱?dāng)你運(yùn)行main()函數(shù)后卻發(fā)現(xiàn)控制臺中沒有任何日志輸出:
這是因?yàn)?strong>GlobalScope.launch函數(shù)每次創(chuàng)建的都是一個頂層協(xié)程,當(dāng)應(yīng)用程序運(yùn)行結(jié)束時頂層協(xié)程也會跟著一起結(jié)束。剛才的日志之所以無法打印出來,正是因?yàn)榇a塊中的代碼還沒來得及運(yùn)行,應(yīng)用程序就結(jié)束了。為了解決這個問題,我們讓程序延遲一段時間再結(jié)束就行了:
fun main() {
GlobalScope.launch {
println("codes run in coroutine scope")
}
// 讓主線程休眠1s(1s后再關(guān)閉應(yīng)用程序)
Thread.sleep(1000)
}
可以看到,在使用Thread.sleep(1000)讓主線程休眠1s后,日志可以正常打印出來了。
可是這種寫法還是存在一些問題的,如果代碼塊中的代碼在1秒鐘內(nèi)不能運(yùn)行結(jié)束,那么就會被強(qiáng)制中斷:
fun main() {
GlobalScope.launch {
println("codes run in coroutine scope")
delay(1500)
println("codes run in coroutine finished")
}
Thread.sleep(1000)
}
我們在代碼塊中加入了一個delay()函數(shù),并在它后面又追加了一條打印。delay()函數(shù)可以讓當(dāng)前協(xié)程延遲一段時間后再運(yùn)行,但它和Thread.sleep()方法不同。delay()函數(shù)只會掛起當(dāng)前協(xié)程,并不會影響其他協(xié)程的運(yùn)行。而Thread.sleep()方法會阻塞當(dāng)前的線程,這樣所有運(yùn)行在該線程下的協(xié)程都會被阻塞。
注意:delay()函數(shù)只能在協(xié)程的作用域或其他掛起函數(shù)中調(diào)用。
這里我們讓協(xié)程掛起1.5s(打印第一行日志1.5s后再打印第二行日志),讓主線程休眠1s(應(yīng)用程序1s后結(jié)束)。重新運(yùn)行程序,你會發(fā)現(xiàn)代碼塊中新增加的一條日志并沒有打印出來,因?yàn)樗€沒來得及運(yùn)行,應(yīng)用程序就已經(jīng)結(jié)束了。為了解決這個問題,我們可以借助runBlocking函數(shù)讓應(yīng)用程序在協(xié)程中所有代碼都運(yùn)行完了之后再結(jié)束:
fun main() {
runBlocking {
println("codes run in coroutine scope")
delay(1500)
println("codes run in coroutine finished")
}
}
runBlocking函數(shù)同樣會創(chuàng)建一個協(xié)程的作用域,但它可以保證所有該協(xié)程作用域內(nèi)的代碼和子協(xié)程,在執(zhí)行完畢之前會一直阻塞當(dāng)前線程。需要注意的是runBlocking函數(shù)通常只應(yīng)該在測試環(huán)境下使用,在正式環(huán)境中使用容易產(chǎn)生一些性能問題。重新運(yùn)行程序,可以看到兩條日志都能夠正常打印出來了:
1.1.1協(xié)程與協(xié)程作用域
- 協(xié)程(Coroutine)是一種輕量級的并發(fā)單元,可以在其執(zhí)行過程中掛起并返回到其父協(xié)程或頂層協(xié)程。協(xié)程的主要特點(diǎn)是它們能夠被掛起并恢復(fù),這使得它們可以用來實(shí)現(xiàn)并發(fā)和異步編程。
- 協(xié)程作用域(Coroutine Scope)則是一種定義協(xié)程生命周期范圍的對象。每個協(xié)程都必須在某個作用域內(nèi)運(yùn)行,當(dāng)作用域被銷毀時,它內(nèi)部的所有協(xié)程都會被自動取消。協(xié)程作用域可以用來管理協(xié)程的啟動、取消和結(jié)構(gòu)化并發(fā)。
簡單來說,協(xié)程是輕量級的并發(fā)單元,而協(xié)程作用域則是定義協(xié)程生命周期范圍的對象,它可以用來管理和控制協(xié)程的執(zhí)行。
1.1.2 使用launch函數(shù)創(chuàng)建子協(xié)程
接下來我們學(xué)習(xí)一下如何使用launch函數(shù)創(chuàng)建多個協(xié)程:
fun main() {
runBlocking {
launch {
println("launch1")
delay(1000)
println("launch1 finished")
}
launch {
println("launch2")
delay(1000)
println("launch2 finished")
}
}
}
這里的launch函數(shù)和我們前面所使用的GlobalScope.launch函數(shù)不同。首先①launch函數(shù)必須在協(xié)程的作用域中才能調(diào)用,其次②launch函數(shù)會在當(dāng)前協(xié)程的作用域下創(chuàng)建子協(xié)程。子協(xié)程的特點(diǎn)是如果外層作用域的協(xié)程結(jié)束了,該作用域下的所有子協(xié)程也會一同結(jié)束,而GlobalScope.launch函數(shù)創(chuàng)建的永遠(yuǎn)是頂層協(xié)程。
我們在runBlocking函數(shù)結(jié)構(gòu)中調(diào)用了兩次launch函數(shù),也就相當(dāng)于創(chuàng)建了兩個子協(xié)程。重新運(yùn)行程序,結(jié)果如下:
可以看到兩個子協(xié)程中的日志是交替打印的,說明它們確實(shí)是像多線程一樣并發(fā)運(yùn)行的。然而這兩個協(xié)程實(shí)際卻運(yùn)行在同一個線程當(dāng)中,只是由編程語言來決定如何在多個協(xié)程之間進(jìn)行調(diào)度。調(diào)度的過程完全不需要操作系統(tǒng)參與,這也就使得協(xié)程的并發(fā)效率會更高。
為了直觀的體驗(yàn)到協(xié)程在處理并發(fā)事件時的效率,我們進(jìn)行以下實(shí)驗(yàn):
fun main() {
// 協(xié)程開始執(zhí)行的時間
val start = System.currentTimeMillis()
runBlocking {
repeat(100000) {
launch {
println(".")
}
}
}
// 協(xié)程執(zhí)行完畢的時間
val end = System.currentTimeMillis()
println("協(xié)程執(zhí)行時間:${end - start}ms")
}
我們使用repeat()函數(shù)創(chuàng)建了10萬個協(xié)程,并讓每個協(xié)程打印一行日志,然后記錄下整個操作的耗時。重新運(yùn)行程序,可以看到我們僅耗時348毫秒,這足以看出協(xié)程是多么的高效。試想一下,如果我們開啟的是10萬個線程,程序或許早都因?yàn)閮?nèi)存泄露而崩潰了。
1.1.3 通過suspend關(guān)鍵聲明掛起函數(shù)
隨著launch函數(shù)中的邏輯越來越復(fù)雜,可能你需要將部分launch函數(shù)中的代碼提取到一個單獨(dú)的函數(shù)中。這個時候就產(chǎn)生了另一個問題:我們在launch函數(shù)中編寫的代碼是擁有協(xié)程作用域的,但是如果將其提取到一個單獨(dú)的函數(shù)中就沒有協(xié)程作用域了,那么我們該如何調(diào)用像delay()這樣的掛起函數(shù)呢?
為此Kotlin提供了一個suspend關(guān)鍵字,使用suspend關(guān)鍵字可以將任意函數(shù)聲明成掛起函數(shù),而掛起函數(shù)之間都是可以相互調(diào)用的:
fun main() {
// 協(xié)程開始執(zhí)行的時間
val start = System.currentTimeMillis()
runBlocking {
repeat(100000) {
launch {
printDot()
}
}
}
// 協(xié)程執(zhí)行完畢的時間
val end = System.currentTimeMillis()
println("協(xié)程執(zhí)行時間:${end - start}ms")
}
// 通過suspend關(guān)鍵字將printDot()聲明成掛起函數(shù)
suspend fun printDot() {
println(".")
delay(1000)
}
這樣printDot()函數(shù)就是一個掛起函數(shù)了,我們也就可以在printDot()函數(shù)中調(diào)用delay()函數(shù)。但suspend關(guān)鍵字只能將一個函數(shù)聲明成掛起函數(shù),卻無法給他提供協(xié)程作用域的。例如你現(xiàn)在嘗試在printDot()函數(shù)中調(diào)用launch函數(shù),一定是無法調(diào)用成功的,因?yàn)?strong>launch函數(shù)要求必須在協(xié)程作用域當(dāng)中才能調(diào)用。
1.1.4 coroutineScope函數(shù)
為了解決suspend關(guān)鍵字作用域的問題,我們可以借助coroutineScope函數(shù)來解決。由于coroutineScope函數(shù)也是一個掛起函數(shù),因此可以在其他掛起函數(shù)中調(diào)用它。coroutineScope函數(shù)的特點(diǎn)是會繼承外部協(xié)程的作用域并創(chuàng)建一個子協(xié)程,借助這個特性,我們可以給任意掛起函數(shù)提供協(xié)程作用域:
suspend fun printDot() = coroutineScope {
launch {
println(".")
delay(1000)
}
}
通過在printDot()內(nèi)部使用coroutineScope函數(shù),使得coroutineScope函數(shù)內(nèi)部啟動的協(xié)程(由launch創(chuàng)建)繼承了printDot()函數(shù)的父作用域(也就是調(diào)用printDot()函數(shù)的外部作用域)。
可以看到,現(xiàn)在我們可以在printDot()這個掛起函數(shù)中調(diào)用launch函數(shù)了。coroutineScope函數(shù)和runBlocking函數(shù)有點(diǎn)類似,它可以保證其作用域內(nèi)的所有代碼和子協(xié)程在全部執(zhí)行完之前,外部的協(xié)程會一直被掛起。例如下面的示例代碼:
fun main() {
// runBlocking確保其作用域內(nèi)所有代碼和子協(xié)程都執(zhí)行完畢前會阻塞當(dāng)前線程
runBlocking {
// coroutineScope確保其作用域內(nèi)所有代碼和子協(xié)程都執(zhí)行完畢前會阻塞當(dāng)前線程
coroutineScope {
launch {
for (i in 1..10) {
println(".")
delay(1000)
}
}
}
println("coroutineScope finished")
}
println("runBlocking finished")
}
我們先使用runBlocking函數(shù)創(chuàng)建了一個協(xié)程作用域,然后這個主協(xié)程作用域內(nèi)又調(diào)用了coroutineScope函數(shù)創(chuàng)建了另一個新的協(xié)程作用域,并等待該作用域內(nèi)的所有子協(xié)程執(zhí)行完畢。運(yùn)行結(jié)果如下:
由此可見coroutineScope函數(shù)確實(shí)是將外部runBlocking協(xié)程掛起了。只有當(dāng)coroutineScope作用域內(nèi)所有的代碼和子協(xié)程都執(zhí)行完畢后,才會執(zhí)行它之后的代碼。需要注意,雖然coroutineScope函數(shù)和runBlocking函數(shù)很類似,但coroutineScope函數(shù)只會阻塞當(dāng)前協(xié)程,既不會影響其他協(xié)程,也不會影響任何線程,因此是不會造成任何性能上的問題的。而runBlocking函數(shù)則會掛起外部線程,如果你恰好又在主線程中調(diào)用runBlocking函數(shù)的話,很可能會造成界面卡死,所以不推薦在實(shí)際項(xiàng)目中體驗(yàn)。
1.2 更多的作用域構(gòu)建器
在上一小節(jié)的內(nèi)容中,我們學(xué)習(xí)了GlobalScope.launch、runBlocking、launch、coroutineScope這幾種作用域構(gòu)建器。它們之間的調(diào)用方法還是有些許不同的:
- GlobalScope.launch{ }函數(shù)、runBlocking{ }函數(shù)可以在任意地方調(diào)用。(實(shí)際項(xiàng)目中不推薦使用)
- coroutineScope{ }函數(shù)可以在協(xié)程作用域或者掛起函數(shù)中調(diào)用。
- launch{ }函數(shù)只能在協(xié)程作用域中調(diào)用。
注意:
①由于runBlocking{ }函數(shù)會阻塞當(dāng)前線程,因此只建議在測試環(huán)境下使用,不建議在實(shí)際項(xiàng)目中使用。
②而GlobalScope.launch{ }函數(shù)每次創(chuàng)建的都是頂層協(xié)程(當(dāng)應(yīng)用程序結(jié)束時頂層協(xié)程也會結(jié)束),除非明確需要創(chuàng)建頂層協(xié)程,否則也不建議在實(shí)際項(xiàng)目中使用。
這里將講一下為什么不建議在實(shí)際項(xiàng)目中使用頂層協(xié)程:
不建議使用的原因是它的管理成本太高了。例如我們在某個Activity中使用協(xié)程發(fā)起了一條網(wǎng)絡(luò)請求,由于網(wǎng)絡(luò)請求是耗時的,當(dāng)用戶在服務(wù)器還沒來得及響應(yīng)的情況下就關(guān)閉了當(dāng)前Activity,按理說此時應(yīng)該取消這條網(wǎng)絡(luò)請求或者不應(yīng)該進(jìn)行回調(diào)操作。因?yàn)锳ctivity已經(jīng)被用戶關(guān)閉了,此時就算服務(wù)器返回了數(shù)據(jù)也沒有任何意義。
然而為了取消頂層協(xié)程,不管是GlobalScope.launch{ }函數(shù)還是launch{ }函數(shù),它們都會返回一個Job對象,我們需要調(diào)用Job對象的cancel()方法來取消協(xié)程:
val job = GlobalScope.launch {
· · ·
}
job.cancel()
如果我們每次創(chuàng)建的都是頂層協(xié)程,那么當(dāng)Activity關(guān)閉時,我們就需要逐個調(diào)用所有已創(chuàng)建協(xié)程的cancel()方法,這簡直是一種災(zāi)難!因此GlobalScope.launch{ }這種協(xié)程作用域構(gòu)建器在實(shí)際項(xiàng)目中也不太常用。
1.2.1 項(xiàng)目中創(chuàng)建協(xié)程的常用方法
下面是實(shí)際項(xiàng)目中比較常見的寫法:
fun main() {
// 創(chuàng)建Job對象
val job = Job()
// 通過Job對象創(chuàng)建CoroutineScope對象
val scope = CoroutineScope(job)
// 通過CoroutineScope對象的launch{}函數(shù)創(chuàng)建協(xié)程
scope.launch {
//處理具體邏輯
}
// 關(guān)閉協(xié)程
job.cancel()
}
我們先是創(chuàng)建了一個Job對象,然后通過這個Job對象來創(chuàng)建一個CoroutineScope對象。之后我們就可以通過調(diào)用這個CoroutineScope對象的launch{ }函數(shù)來創(chuàng)建一個協(xié)程了。現(xiàn)在所有調(diào)用CoroutineScope的launch{ }函數(shù)所創(chuàng)建協(xié)程,都會被關(guān)聯(lián)在Job對象的作用域下面?,F(xiàn)在我們只需要調(diào)用一次Job對象中的cancel()方法,就可以將同一作用域內(nèi)的所有協(xié)程全部取消,從而很大程度上降低了協(xié)程管理的成本。
總而言之,CoroutineScope( )函數(shù)更適合實(shí)際項(xiàng)目當(dāng)中使用。如果你只是在Main()函數(shù)中編寫一些學(xué)習(xí)測試用的代碼,還是使用runBlocking{ }函數(shù)最為方便。
1.2.2 獲取協(xié)程的返回值
通過前面的學(xué)習(xí)你已經(jīng)知道了調(diào)用launch{ }函數(shù)可以創(chuàng)建一個新的協(xié)程,但是launch{ }函數(shù)只能用于執(zhí)行一段邏輯,卻不能獲取執(zhí)行的結(jié)果,因?yàn)樗姆祷刂涤肋h(yuǎn)是一個Job對象。那么如何能夠創(chuàng)建一個協(xié)程并獲取它的執(zhí)行結(jié)果呢?
其實(shí)我們通過async函數(shù)就可以實(shí)現(xiàn),async函數(shù)必須在協(xié)程作用域當(dāng)中才能調(diào)用。async函數(shù)會創(chuàng)建一個新的協(xié)程并返回一個Deferred對象,如果我們想要獲取async函數(shù)代碼塊的執(zhí)行結(jié)果,只需要調(diào)用Deferred對象的await()方法即可。下面是一段示例代碼:
fun main() {
runBlocking {
val result = async {
5 + 5
}.await()
println(result)
}
}
我們通過async()函數(shù)將運(yùn)算結(jié)果保存了下來,然后打印到日志中。
在調(diào)用了async函數(shù)之后,代碼塊中的代碼就會立刻開始執(zhí)行。當(dāng)調(diào)用await()方法時,如果async函數(shù)體中的代碼還沒有執(zhí)行完畢,那么當(dāng)前協(xié)程(即 runBlocking 函數(shù)內(nèi)的協(xié)程)會被掛起,直到可以獲得async函數(shù)的執(zhí)行結(jié)果。我們接下來編寫一段代碼進(jìn)行驗(yàn)證:
fun main() {
runBlocking {
// 開始時間
val start = System.currentTimeMillis()
// result1延時1s
val result1 = async {
delay(1000)
5 + 5
}.await()
// result2延時1s
val result2 = async {
delay(1000)
4 + 6
}.await()
println("result is : ${result1 + result2}")
// 結(jié)束時間
val end = System.currentTimeMillis()
println("一共耗時 : ${end - start}ms")
}
}
這里我們使用了兩個async函數(shù)來執(zhí)行任務(wù),在每個async代碼塊中調(diào)用delay()方法進(jìn)行1秒的延時,然后在async代碼塊執(zhí)行完畢后都調(diào)用了await()方法。按照我們之前的學(xué)習(xí),await()方法使得async代碼塊中的代碼在執(zhí)行完畢之前會一直阻塞當(dāng)前協(xié)程。運(yùn)行程序后可以看到如下打印,足以說明協(xié)程是順序執(zhí)行的,即等待result1執(zhí)行完畢后再執(zhí)行result2的。
但是上面這種寫法效率是很低的,因?yàn)槊總€async塊執(zhí)行完后都調(diào)用了await()方法。這意味著,第一個async塊中的代碼在執(zhí)行完畢后必須等待1秒后才去執(zhí)行第二個async塊。而第二個async塊同樣會等待1秒后才去執(zhí)行主線程中的其他代碼,所以總的執(zhí)行時間約為2秒。其實(shí)兩個async函數(shù)完全可以同時執(zhí)行,從而提高運(yùn)行效率。現(xiàn)在對上述代碼進(jìn)行優(yōu)化:
fun main() {
runBlocking {
val start = System.currentTimeMillis()
val deferred1 = async {
delay(1000)
5 + 5
}
val deferred2 = async {
delay(1000)
4 + 6
}
// 在使用返回值的地方再調(diào)用await()方法
println("result is : ${deferred1.await() + deferred2.await()}")
val end = System.currentTimeMillis()
println("一共耗時 : ${end - start}ms")
}
}
可以看到在這段代碼中,我們改變了調(diào)用await()函數(shù)的時機(jī),僅在需要用到async代碼塊的執(zhí)行結(jié)果時才調(diào)用await()方法去獲取。這樣兩個async函數(shù)就可以并行執(zhí)行了,第二個async再也不用等待第一個async完成之后才能執(zhí)行了。這次重新運(yùn)行程序:
可以看到我們的代碼耗時從2015ms縮短為了1015ms,運(yùn)行效率顯著提升。這也就說明我們兩個async中的代碼確實(shí)是并行執(zhí)行的,并且成功將他們的結(jié)果輸出到日志中了。
1.2.3 withContext函數(shù)
最后我們再來學(xué)習(xí)一個比較特殊的作用域構(gòu)建器——withContext( )函數(shù)。withContext()函數(shù)也是一個掛起函數(shù),可以將其理解成async函數(shù)的一種簡化版寫法。示例代碼如下:
fun main() {
runBlocking {
val result = withContext(Dispatchers.Default) {
5 + 5
}
println(result)
}
}
當(dāng)我們調(diào)用withContext()函數(shù)后,會立即執(zhí)行代碼塊中的代碼,同時會將外部協(xié)程(runBlocking{ })掛起。當(dāng)代碼塊中的代碼全部執(zhí)行完畢后,會將最后一行的執(zhí)行結(jié)果作為withContext()函數(shù)的返回值進(jìn)行返回。因此基本相當(dāng)于val result = async{ 5 + 5 }.await()的寫法,唯一不同的是withContext()函數(shù)會強(qiáng)制要求我們指定一個線程參數(shù)。
你已經(jīng)知道協(xié)程是一種輕量級的線程,因此很多傳統(tǒng)編程情況下需要開啟多線程執(zhí)行并發(fā)任務(wù)。然而借助協(xié)程,我們只需要在一個線程中開啟多個協(xié)程來執(zhí)行就可以了。這并不意味著我們就永遠(yuǎn)不需要開啟線程了,比如說Android中的網(wǎng)絡(luò)請求必須要在子線程中進(jìn)行,即使你開啟了協(xié)程去執(zhí)行網(wǎng)絡(luò)請求,假如它是主線程當(dāng)中的協(xié)程,那么程序依然會報錯。這個時候我們就應(yīng)該通過線程參數(shù)給協(xié)程指定一個具體運(yùn)行的線程。
線程參數(shù)主要有以下3種值可以選擇:Dispatchers.Default、Dispatchers.IO、Dispatchers.Main。文章來源:http://www.zghlxwxcb.cn/news/detail-772187.html
- Dispatchers.Default:使用一種默認(rèn)低并發(fā)的線程策略,當(dāng)你要執(zhí)行的代碼屬于計算密集型任務(wù)時,開啟過高的并發(fā)反而可能會影響任務(wù)的運(yùn)行效率,因此就可以使用Dispatchers.Default。
- Dispatchers.IO:使用一種較高并發(fā)的線程策略,當(dāng)你要執(zhí)行的代碼大多數(shù)時間是在阻塞和等待中,比如說要執(zhí)行網(wǎng)絡(luò)請求時,為了能夠支持更高的并發(fā)數(shù)量,此時就可以使用Dispatchers.IO。
- Dispatchers.Main:表示不會開啟子線程,而是在Android主線程中執(zhí)行代碼,但是這個值只能在Android項(xiàng)目中使用,純Kotlin程序使用這種類型的線程參數(shù)會報錯。
事實(shí)上除了coroutineScope{ }函數(shù)之外,其他所有的函數(shù)都是可以指定這樣一個線程參數(shù)的,只不過withContext()函數(shù)是強(qiáng)制要求指定的,而其他函數(shù)則是可選的。文章來源地址http://www.zghlxwxcb.cn/news/detail-772187.html
到了這里,關(guān)于【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!