欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Kotlin啟動(dòng)協(xié)程的三種方式示例詳解

 更新時(shí)間:2022年12月08日 09:44:59   作者:無糖可樂愛好者  
這篇文章主要為大家介紹了Kotlin啟動(dòng)協(xié)程的三種方式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1.launch啟動(dòng)協(xié)程

fun main() = runBlocking {
    launch { 
        delay(1000L) 
        println("World!") 
    }
    println("Hello") 
}
fun main() {
    GlobalScope.launch {
        delay(1000L)
        println("World!")
    }
    println("Hello")
    Thread.sleep(2000L)
}
//輸出結(jié)果
//Hello
//World!

上面是兩段代碼,這兩段代碼都是通過launch啟動(dòng)了一個(gè)協(xié)程并且輸出結(jié)果也是一樣的。

第一段代碼中的runBlocking是協(xié)程的另一種啟動(dòng)方式,這里先看第二段代碼中的launch的啟動(dòng)方式;

  • GlobalScope.launch

GlobalScope.launch是一個(gè)擴(kuò)展函數(shù),接收者是CoroutineScope,意思就是協(xié)程作用域,這里的launch等價(jià)于CoroutineScope的成員方法,如果要調(diào)用launch來啟動(dòng)一個(gè)協(xié)程就必須先拿到CoroutineScope對(duì)象。GlobalScope.launch源碼如下

public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

里面有三個(gè)參數(shù):

  • context: 意思是上下文,默認(rèn)是EmptyCoroutineContext,有默認(rèn)值就可以不傳,但是也可以傳遞Kotlin提供的Dispatchers來指定協(xié)程運(yùn)行在哪一個(gè)線程中;
  • start: CoroutineStart代表了協(xié)程的啟動(dòng)模式,不傳則默認(rèn)使用DEFAULT(根據(jù)上下文立即調(diào)度協(xié)程執(zhí)行),除DEFAULT外還有其他類型:

LAZY:延遲啟動(dòng)協(xié)程,只在需要時(shí)才啟動(dòng)。

ATOMIC:以一種不可取消的方式,根據(jù)其上下文安排執(zhí)行的協(xié)程;

UNDISPATCHED:立即執(zhí)行協(xié)程,直到它在當(dāng)前線程中的第一個(gè)掛起點(diǎn);

  • block: suspend是掛起的意思,CoroutineScope.()是一個(gè)擴(kuò)展函數(shù),Unit是一個(gè)函數(shù)類似于Java的void,那么suspend CoroutineScope.() -> Unit就可以這么理解了:首先,它是一個(gè)掛起函數(shù),然后它還是CoroutineScope類的成員或者擴(kuò)展函數(shù),參數(shù)為空,返回值類型為Unit。
  • delay(): delay()方法從字面理解就是延遲的意思,在上面的代碼中延遲了1秒再執(zhí)行World,從源碼可以看出來它跟其他方法不一樣,多了一個(gè)suspend關(guān)鍵字
//      掛起
//       ↓
public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        // if timeMillis == Long.MAX_VALUE then just wait forever like awaitCancellation, don't schedule.
        if (timeMillis < Long.MAX_VALUE) {
            cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
        }
    }
}

suspend的意思就是掛起,被它修飾的函數(shù)就是掛起函數(shù), 這也就意味著delay()方法具有掛起和恢復(fù)的能力;

  • Thread.sleep(2000L)

這個(gè)是休眠2秒,那么這里為什么要有這個(gè)呢?要解答這疑問其實(shí)不難,將Thread.sleep(2000L)刪除后在運(yùn)行代碼可以發(fā)現(xiàn)只打印了Hello然后程序就結(jié)束了,World!并沒有被打印出來。

為什么? 將上面的代碼轉(zhuǎn)換成線程實(shí)現(xiàn)如下:

fun main() {
    thread(isDaemon = true) {
        Thread.sleep(1000L)
        println("Hello World!")
    }
}

如果不添加isDaemon = true結(jié)果輸出正常,如果加了那么就沒有結(jié)果輸出。isDaemon的加入后其實(shí)是創(chuàng)建了一個(gè)【守護(hù)線程】,這就意味著主線程結(jié)束的時(shí)候它會(huì)跟著被銷毀,所以對(duì)于將Thread.sleep刪除后導(dǎo)致GlobalScope創(chuàng)建的協(xié)程不能正常運(yùn)行的主要原因就是通過launch創(chuàng)建的協(xié)程還沒開始執(zhí)行程序就結(jié)束了。那么Thread.sleep(2000L)的作用就是為了不讓主線程退出。

另外這里還有一點(diǎn)需要注意:程序的執(zhí)行過程并不是按照順序執(zhí)行的。

fun main() {
    GlobalScope.launch {                // 1
        println("Launch started!")      // 2
        delay(1000L)                    // 3
        println("World!")         	   // 4
    }
    println("Hello")            		// 5
    Thread.sleep(2000L)                 // 6
    println("Process end!")             // 7
}
/*
輸出結(jié)果:
Hello
Launch started!
World!
Process end!
*/

上面的代碼執(zhí)行順序是1、5、6、2、3、4、7,這個(gè)其實(shí)好理解,首先執(zhí)行1,然后再執(zhí)行5,執(zhí)行6的時(shí)候等待2秒,在這個(gè)等待過程中協(xié)程創(chuàng)建完畢了開始執(zhí)行2、3、4都可以執(zhí)行了,當(dāng)2、3、4執(zhí)行完畢后等待6執(zhí)行完畢,最后執(zhí)行7,程序結(jié)束。

2.runBlocking啟動(dòng)協(xié)程

fun main() {
    runBlocking {                // 1
        println("launch started!")      // 2
        delay(1000L)           // 3
        println("World!")         	    // 4
    }
    println("Hello")            		// 5
    Thread.sleep(2000L)           // 6
    println("Process end!")             // 7
}

上面這段代碼只是將GlobalScope.launch改成了runBlocking,但是執(zhí)行順序卻完全不一樣,它的執(zhí)行順訊為代碼順序1~7,這是因?yàn)?code>runBlocking是帶有阻塞屬性的,它會(huì)阻塞當(dāng)前線程的執(zhí)行。這是它跟launch的最大差異。

runBlockinglanuch的另外一個(gè)差異是GlobalScope,從代碼中可以看出runBlocking并不需要這個(gè),這點(diǎn)可以從源碼中分析

public actual fun <T> runBlocking(
    context: CoroutineContext, 
    block: suspend CoroutineScope.() -> T): T {
    ...
}

頂層函數(shù):類似于Java中的靜態(tài)函數(shù),在Java中常用與工具類,例如StringUtils.lastElement();

runBlocking是一個(gè)頂層函數(shù),因此可以直接使用它;在它的第二個(gè)參數(shù)block中有一個(gè)返回值類型:T,它剛好跟runBlocking的返回值類型是一樣的,因此可以推測(cè)出runBlocking是可以有返回值的

fun main() {
    val result = test(1)
    println("result:$result")
}
fun test(num: Int) = runBlocking {
    return@runBlocking num.toString()
}
//輸出結(jié)果:
//result:1

但是,Kotlin在文檔中注明了這個(gè)函數(shù)不應(yīng)該從協(xié)程中使用。它的設(shè)計(jì)目的是將常規(guī)的阻塞代碼與以掛起風(fēng)格編寫的庫連接起來,以便在主函數(shù)和測(cè)試中使用。 因此在正式環(huán)境中這種方式最好不用。

3.async啟動(dòng)協(xié)程

在 Kotlin 當(dāng)中,可以使用 async{} 創(chuàng)建協(xié)程,并且還能通過它返回的句柄拿到協(xié)程的執(zhí)行結(jié)果。

fun main() = runBlocking {
    val deferred = async {
        1 + 1
    }
    println("result:${deferred.await()}")
}
//輸出結(jié)果:
//result:2

上面的代碼啟動(dòng)了兩個(gè)協(xié)程,啟動(dòng)方式是runBlockingasync,因?yàn)?code>async的調(diào)用需要一個(gè)作用域,而runBlocking恰好滿足這個(gè)條件,GlobalScope.launch也可以滿足這個(gè)條件但是GlobalScope也不建議在生產(chǎn)環(huán)境中使用,因?yàn)?code>GlobalScope 創(chuàng)建的協(xié)程沒有父協(xié)程,GlobalScope 通常也不與任何生命周期組件綁定。除非手動(dòng)管理,否則很難滿足我們實(shí)際開發(fā)中的需求。

上面的代碼多了一個(gè)deferred.await()它就是獲取最終結(jié)果的關(guān)鍵。

public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T> {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyDeferredCoroutine(newContext, block) else
        DeferredCoroutine<T>(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}

asynclaunch一樣也是一個(gè)擴(kuò)展函數(shù),也有三個(gè)參數(shù),和launch的區(qū)別在于兩點(diǎn):

  • block的函數(shù)類型: launch返回的是Unit類型,async返回的是泛型T
  • 返回值不同: launch返回的是Job,async返回的是Deffered<T>,而async可以返回執(zhí)行結(jié)果的關(guān)鍵就在這里。

啟動(dòng)協(xié)程的三種方式都講完了,這里存在一個(gè)疑問,launchasync都有返回值,為什么async可以獲取執(zhí)行結(jié)果,launch卻不行?

這主要跟launch的返回值有關(guān),launch的返回值Job代表的是協(xié)程的句柄,而句柄并不能返回協(xié)程的執(zhí)行結(jié)果。

句柄: 句柄指的是中間媒介,通過這個(gè)中間媒介可以控制、操作某樣?xùn)|西。舉個(gè)例子,door handle 是指門把手,通過門把手可以去控制門,但 door handle 并非 door 本身,只是一個(gè)中間媒介。又比如 knife handle 是刀柄,通過刀柄可以使用刀。

協(xié)程的三中啟動(dòng)方式區(qū)別如下:

  • launch:無法獲取執(zhí)行結(jié)果,返回類型Job,不會(huì)阻塞;
  • async:可獲取執(zhí)行結(jié)果,返回類型Deferred,調(diào)用await()會(huì)阻塞不調(diào)用則不會(huì)但也無法獲取執(zhí)行結(jié)果;
  • runBlocking:可獲取執(zhí)行結(jié)果,阻塞當(dāng)前線程的執(zhí)行,多用于Demo、測(cè)試,官方推薦只用于連接線程與協(xié)程。

以上就是Kotlin啟動(dòng)協(xié)程的三種方式示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Kotlin啟動(dòng)協(xié)程方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論