Kotlin-26-協程的參數

目錄

1、源碼

2、CoroutineContext

3、CoroutineDispatcher

3.1、代碼實現

4、CoroutineStart

4.1、代碼實現

4.2、指定調度器CoroutineDispatcher後四種啓動模式都無法取消?

5、CoroutineScope


1、源碼

 launch協程中總共有三個參數(async的參數與launch相同),如下:

CoroutineContext       協程的上下文,EmptyCoroutineContext 表示一個空的協程上下文
CoroutineStart         協程的啓動方式,它四種標準方式
suspend CoroutineScope 協程的lambda函數體

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
}

2、CoroutineContext

協程上下文,主要用來調度協程本身。

CoroutineContext本身是一個接口,它的繼承關係:

CoroutineContext--->Element--->ContinuationInterceptor---->CoroutineDispatcher

3、CoroutineDispatcher

協程調度器,決定協程所在的線程或線程池。它可以指定協程運行於特定的一個線程、一個線程池,如果不指定任何線程(這樣協程就會運行於當前線程)。

launch函數定義如果不指定CoroutineDispatcher或者沒有其他的ContinuationInterceptor,默認的協程調度器就是Dispatchers.DefaultDefault是一個協程調度器,其指定的線程爲共有的線程池,線程數量至少爲 2 最多與 CPU 數相同, 對於消耗CPU資源的計算密集型協程,這是一個合適的選擇。

 CoroutineDispatcher 有四種標準實現

  • Dispatchers.Default
  • Dispatchers.IO
  • Dispatchers.Main
  • Dispatchers.Unconfined,Unconfined 就是不指定線程。
  • newSingleThreadContext 爲協程的運行啓動了一個線程。 一個專用的線程是一種非常昂貴的資源。 在真實的應用程序中兩者都必須被釋放,當不再需要的時候,使用 close 函數,或存儲在一個頂層變量中使它在整個應用程序中被重用。
public object Dispatchers {
    /**
     * 它由JVM上的共享線程池支持。 默認情況下,使用的最大並行度爲此調度程序的CPU內核數,但至少爲兩個。 
     */
    public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
    /**
     * 協程分派器,僅限於使用UI對象操作的Main線程。可以直接使用此分派器,也可以通過[MainScope]工廠使用。
     * 通常,此類調度程序是單線程的。
     */
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
    /**
     * 不侷限於任何特定線程的協程調度程序。也就是哪個線程調用了該協程,就在該線程中運行。
     * 但是只是在第一個掛起點之前是這樣的,掛起恢復後運行在哪個線程完全由所調用的掛起函數決定。
     * 非受限的調度器非常適用於執行不消耗 CPU 時間的任務,以及不更新侷限於特定線程的任何共享數據(如UI)的協程。
     * 注意:非受限的調度器是一種高級機制,可以在某些極端情況下提供幫助而不需要調度協程以便稍後執行或產生不希望的副作用, 
     * 因爲某些操作必須立即在協程中執行。 非受限調度器不應該在通常的代碼中使用。
     */
    public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
    /**
     * 這個調度器設計用於將阻塞的IO任務轉移到共享線程池。將在此池中創建IO操作的線程,並根據需要將其關閉。
     * 此調度程序使用的線程數受系統屬性“ kotlinx.coroutines.io.parallelism”的值限制。
     * 它默認爲64個線程或內核數(以較大者爲準)的限制。
     */
    public val IO: CoroutineDispatcher = DefaultScheduler.IO
}

3.1、代碼實現

fun main() {
    val job1 = GlobalScope.launch(Dispatchers.Unconfined) {
        println("job1所在的線程,name=${Thread.currentThread().name}")
        delay(1000)
        println("job1 delay後所在的線程,name=${Thread.currentThread().name}")

    }
    Thread(Runnable {
        val block = runBlocking {
            val job2 = launch(Dispatchers.Default, CoroutineStart.DEFAULT) {
                println("job2所在的線程,name=${Thread.currentThread().name}")
            }
            val job3 = launch(Dispatchers.IO) {
                println("job3所在的線程,name=${Thread.currentThread().name}")
            }
            val job4 = launch(Dispatchers.Unconfined) {
                println("job4所在的線程,name=${Thread.currentThread().name}")
                delay(1000)
                println("job4 delay後所在的線程,name=${Thread.currentThread().name}")
            }
            val job5 = launch(newSingleThreadContext("newThread1")) {
                println("job5所在的線程,name=${Thread.currentThread().name}")
            }
        }
    }).start()
}
//輸出結果
job1所在的線程,name=main
job2所在的線程,name=DefaultDispatcher-worker-1
job3所在的線程,name=DefaultDispatcher-worker-1
job4所在的線程,name=Thread-0
job5所在的線程,name=newThread1
job1 delay後所在的線程,name=kotlinx.coroutines.DefaultExecutor
job4 delay後所在的線程,name=kotlinx.coroutines.DefaultExecutor

4、CoroutineStart

協程啓動的方式,協程啓動選項有以下四種:

  •   [DEFAULT]-根據其上下文立即安排協程執行;
  •   [LAZY]-僅在需要時才延遲啓動協程;
  •   [ATOMIC]-原子地(以不可取消的方式)根據協程的上下文來調度協程以使其執行;這類似於[DEFAULT],但協程不能在開始執行之前被取消。
  •   [UNDISPATCHED]-立即執行協程,直到其在當前線程_中的第一個掛起點_爲止。就像協程已使用[Dispatchers.Unconfined]啓動一樣。 但是,當協程從暫停狀態恢復時,它會根據其上下文中的[CoroutineDispatcher]進行調度。這與[ATOMIC]相似,即使協程已被取消,協程也開始執行,但是區別在於協程開始在同一線程中執行。

4.1、代碼實現

從下面的代碼看出:

  • LAZY啓動模式的協程,創建之後,不會立即執行,只在調用了它的start()或者join()方法的時候,纔會執行。
  • ATOMIC在調用了它的cancel()函數後,照樣可以執行
  • DEFAULT調用cancel()函數後,無法執行
  • UNDISPATCHED調用了它的cancel()函數後,也可以執行
fun main() {

    val block = runBlocking {
        val job1 = launch(start = CoroutineStart.DEFAULT) {
            println("job1執行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
        }
        val job2 = launch(start = CoroutineStart.LAZY) {
            println("job2執行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
        }
        val job3 = launch(start = CoroutineStart.ATOMIC) {
            println("job3執行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
        }
        val job4 = launch(start =  CoroutineStart.UNDISPATCHED) {
            println("job4執行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
        }

        println("執行了取消命令-----------time=${Date()}")
        job1.cancel()
        job3.cancel()
        job4.cancel()
        
        println("job1.isCancelled=${job1.isCancelled}")
        println("job2.isCancelled=${job2.isCancelled}")
        println("job3.isCancelled=${job3.isCancelled}")
        println("job4.isCancelled=${job4.isCancelled}")
        
        delay(2000)
        println("啓動job2的執行命令")
        job2.start() //job2.start()
    }
}
//輸出結果
job4執行了----------time=Fri Dec 27 14:28:41 CST 2019---------------------------main
執行了取消命令-----------time=Fri Dec 27 14:28:41 CST 2019
job1.isCancelled=true
job2.isCancelled=false
job3.isCancelled=true
job4.isCancelled=false
job3執行了----------time=Fri Dec 27 14:28:41 CST 2019---------------------------main
啓動job2的執行命令
job2執行了----------time=Fri Dec 27 14:28:43 CST 2019---------------------------main

4.2、指定調度器CoroutineDispatcher後四種啓動模式都無法取消?

fun main() {

    val block = runBlocking {
        val job1 = launch(Dispatchers.Default,start = CoroutineStart.DEFAULT) {
            println("job1執行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
        }
        val job2 = launch(Dispatchers.Default,start = CoroutineStart.LAZY) {
            println("job2執行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
        }
        val job3 = launch(Dispatchers.Default,start = CoroutineStart.ATOMIC) {
            println("job3執行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
        }
        val job4 = launch(Dispatchers.Default,start =  CoroutineStart.UNDISPATCHED) {
            println("job4執行了----------time=${Date()}---------------------------${Thread.currentThread().name}")
        }

        println("執行了取消命令-----------time=${Date()}")
        job1.cancel()
        job3.cancel()
        job4.cancel()

        delay(2000)
        println("啓動job2的執行命令")
        println("job1.isCancelled=${job1.isCancelled}")
        println("job2.isCancelled=${job2.isCancelled}")
        println("job3.isCancelled=${job3.isCancelled}")
        println("job4.isCancelled=${job4.isCancelled}")
        job2.start() //job2.start()
    }
}
//輸出結果
job3執行了----------time=Fri Dec 27 14:38:39 CST 2019---------------------------DefaultDispatcher-worker-2
job4執行了----------time=Fri Dec 27 14:38:39 CST 2019---------------------------main
job1執行了----------time=Fri Dec 27 14:38:39 CST 2019---------------------------DefaultDispatcher-worker-1
執行了取消命令-----------time=Fri Dec 27 14:38:39 CST 2019
啓動job2的執行命令
job1.isCancelled=false
job2.isCancelled=false
job3.isCancelled=false
job4.isCancelled=false
job2執行了----------time=Fri Dec 27 14:38:41 CST 2019---------------------------DefaultDispatcher-worker-1

5、CoroutineScope

協程的第三個參數,它是一個lambda函數,也是協程需要執行的函數體部分。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

發佈了88 篇原創文章 · 獲贊 16 · 訪問量 26萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章