天天看點

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.Default

Default

是一個協程排程器,其指定的線程為共有的線程池,線程數量至少為 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函數,也是協程需要執行的函數體部分。
Kotlin-26-協程的參數

繼續閱讀