国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序

這篇具有很好參考價值的文章主要介紹了【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

提示:此文章僅作為本人記錄日常學(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)控制臺中沒有任何日志輸出:
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android

這是因?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后,日志可以正常打印出來了。
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android
可是這種寫法還是存在一些問題的,如果代碼塊中的代碼在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)行程序,可以看到兩條日志都能夠正常打印出來了:
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android

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é)果如下:
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android
可以看到兩個子協(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)存泄露而崩潰了。
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android

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é)果如下:
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android
由此可見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é)果保存了下來,然后打印到日志中。
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android
在調(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的。
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android
但是上面這種寫法效率是很低的,因?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)行程序:
【28】Kotlin語法進(jìn)階——使用協(xié)程編寫高效的并發(fā)程序,奇妙的Kotlin之旅,kotlin,開發(fā)語言,android
可以看到我們的代碼耗時從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。

  • 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)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • Kotlin 協(xié)程基礎(chǔ)使用學(xué)習(xí)

    Kotlin 協(xié)程基礎(chǔ)使用學(xué)習(xí)

    原文: Kotlin 協(xié)程基礎(chǔ)使用學(xué)習(xí)-Stars-One的雜貨小窩 本篇閱讀可能需要以下知識,否則可能閱讀會有些困難 客戶端開發(fā)基礎(chǔ)(Android開發(fā)或JavaFx開發(fā)) Java多線程基礎(chǔ) kotlin基礎(chǔ) 本文盡量以使用為主,以代碼為輔講解,不提及過深協(xié)程底層代碼邏輯,僅做一個基礎(chǔ)入門來快速上手學(xué)習(xí)(斷斷

    2024年03月18日
    瀏覽(53)
  • Kotlin 協(xié)程 asyn和launch簡單使用

    在 Kotlin 協(xié)程中, async 和 launch 都是協(xié)程構(gòu)建器,但它們有一些重要的區(qū)別: 返回值: launch 函數(shù)返回一個 Job 對象,代表一個在后臺執(zhí)行的任務(wù)。 Job 可以用于等待任務(wù)完成、取消任務(wù)等操作。 async 函數(shù)返回一個 Deferred 對象,它是 Job 的子接口,并且包含一個計算結(jié)果。您可

    2024年01月19日
    瀏覽(27)
  • Kotlin 協(xié)程與生命周期感知組件一起使用

    應(yīng)用中的每一個 ViewModel 都定義了 ViewModelScope 。如果 ViewModel 已清除,則在此范圍內(nèi)啟動的協(xié)程都會取消。如果您具有僅在 ViewModel 處于活動的狀態(tài)時才需要完成的工作。此時協(xié)程非常有用。如果要為布局計算某些數(shù)據(jù),則應(yīng)將工作范圍限定至 ViewModel ,以便在 ViewModel 清除后,

    2024年02月11日
    瀏覽(17)
  • Android kotlin實(shí)戰(zhàn)之協(xié)程suspend詳解與使用

    Android kotlin實(shí)戰(zhàn)之協(xié)程suspend詳解與使用

    ????????Kotlin 是一門僅在標(biāo)準(zhǔn)庫中提供最基本底層 API 以便各種其他庫能夠利用協(xié)程的語言。與許多其他具有類似功能的語言不同, async ?與? await ?在 Kotlin 中并不是,甚至都不是標(biāo)準(zhǔn)庫的一部分。此外,Kotlin 的? 掛起函數(shù) ?概念為異步操作提供了比 future 與 pro

    2024年02月03日
    瀏覽(23)
  • Android使用kotlin+協(xié)程+room數(shù)據(jù)庫的簡單應(yīng)用

    Android使用kotlin+協(xié)程+room數(shù)據(jù)庫的簡單應(yīng)用

    前言:一般主線程(UI線程)中是不能執(zhí)行創(chuàng)建數(shù)據(jù)這些操作的,因?yàn)榈却龝r間長。所以協(xié)程就是為了解決這個問題出現(xiàn)。 第一步:在模塊級的build.gradle中引入 ? 好了前期工作ok,正式編寫room吧! 第二步:創(chuàng)建表實(shí)體 ?第三部:編寫對應(yīng)的Dao接口 ?第四步:創(chuàng)建數(shù)據(jù)庫信息

    2024年02月13日
    瀏覽(24)
  • Kotlin 協(xié)程一 —— 協(xié)程 Coroutine

    1.1.1基本定義 進(jìn)程 進(jìn)程是一個具有一定獨(dú)立功能的程序在一個數(shù)據(jù)集上的一次動態(tài)執(zhí)行的過程,是操作系統(tǒng)進(jìn)行資源分配和調(diào)度的一個獨(dú)立單位,是應(yīng)用程序運(yùn)行的載體。 進(jìn)程是資源分配的最小單位,在單核CPU中,同一時刻只有一個程序在內(nèi)存中被CPU調(diào)用運(yùn)行。 線程 基本的

    2024年02月05日
    瀏覽(20)
  • 使用 Kotlin DSL 編寫網(wǎng)絡(luò)爬蟲

    本博文將會通過一個網(wǎng)絡(luò)爬蟲的例子,向你介紹 Kotlin 的基本用法和其簡潔有力的 DSL。 按照維基百科的說法,DSL(domain-specific language) 是一種專注于某一特定應(yīng)用領(lǐng)域的計算機(jī)語言。和我們常用的通用目的型語言(類如 C,Java,Python 等)相反,DSL 并不承諾可用來解決一切可計

    2024年03月26日
    瀏覽(24)
  • Kotlin協(xié)程-從一到多

    上一篇文章,我介紹了Kotlin協(xié)程的創(chuàng)建,使用,協(xié)作等內(nèi)容。本篇將引入更多的使用場景,繼續(xù)帶你走進(jìn)協(xié)程世界。 常用編程語言都會內(nèi)置對同一類型不同對象的數(shù)據(jù)集表示,我們通常稱之為容器類。不同的容器類適用于不同的使用場景。Kotlin的 Flow 就是在異步計算的需求下

    2024年02月09日
    瀏覽(20)
  • Kotlin協(xié)程學(xué)習(xí)之-02

    協(xié)程的基本使用 GlobalScope.launch 生命周期與進(jìn)程一致,且無法取消 runBlocking 會阻塞線程,一般在測試階段可以使用 val coroutineScope = CoroutineScope(context) coroutineScope.launch 通過context參數(shù)去管理和控制協(xié)程的生命周期 用法 val coroutineScope = CoroutineScope(context) coroutineScope.launch(Dispatche

    2024年01月22日
    瀏覽(22)
  • Android Kotlin 協(xié)程初探

    維基百科:協(xié)程,英文Coroutine [k?ru’tin] (可入廳),是計算機(jī)程序的一類組件,推廣了協(xié)作式多任務(wù)的子程序,允許執(zhí)行被掛起與被恢復(fù)。 作為Google欽定的Android開發(fā)首選語言Kotlin,協(xié)程并不是 Kotlin 提出來的新概念,目前有協(xié)程概念的編程語言有Lua語言、Python語言、Go語言

    2024年02月08日
    瀏覽(27)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包