Kotlin学习笔记之 29 上下文与调度器

1,108 阅读3分钟

首发于公众号: DSGtalk1989

29.上下文与调度器

  • 调度器与线程

    前面我们说到launch提供了3个可选的参数,分别是上下文,启动模式和协程函数。

    其中的第一个上下文CoroutineContext,我们点开来详细的看一下。

    @SinceKotlin("1.3")
    public interface CoroutineContext 
    

    是个接口,我们继续看在这个文件的最下面,有这样一段代码,继续一个接口Element实现了协程上下文的接口。

    public interface Element : CoroutineContext
    

    我们尝试着点击一下Elementfind usages,能看到有个文件叫做CoroutineContextImpl.kt文件,这个文件看上去是协程上下文的实现类。我们具体看下。

    • AbstractCoroutineContextElement

    public abstract class AbstractCoroutineContextElement(public override val key: Key<*>) : Element

    抽象类,实现了`Element`,等于实现了`CoroutineContext`。所以继承这个抽象类的在我们看来也可以放到我们的`launch`方法中,充当协程上下文参数。
    
    * `EmptyCoroutineContext`
    
       ```js
    public object EmptyCoroutineContext : CoroutineContext, Serializable 
    

    这个就是launch方法中的默认参数,就像他的注释上写的

    An empty coroutine context.

    一个空的协程上下文

    还有一个内部实现,一般情况下我们不使用他。那么目前看上去能够让我们可以去定制化的往launch方法当中传协程上下文的是来自于AbstractCoroutineContextElement的子类,我们再次find usages一下。

    发现了对象,私有类,module内部类等等,就发现一个可以用的是CoroutineDispatcher协程的分发者

    public abstract class CoroutineDispatcher :
      AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor 
    

    继续追,我们在CoroutineDispatcher的注释中看到描述的最多的指向Dispatchers,点开来,看到四个很熟悉的名字DefaultMainUnconfinedIO,我们尝试针对这四个名词的注释做一些理解。

    /**
    * It is backed by a shared pool of threads on JVM
    */
    @JvmStatic
      public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
      
    /**
    * In order to work with `Main` dispatcher, following artifact should be added to project runtime dependencies:
       *  - `kotlinx-coroutines-android` for Android Main thread dispatcher
    */
    @JvmStatic
      public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher 
    
    /**
    * A coroutine dispatcher that is not confined to any specific thread.
    */
    @JvmStatic
      @ExperimentalCoroutinesApi
      public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined 
      
    
    /**
    * The [CoroutineDispatcher] that is designed for offloading blocking IO tasks to a shared pool of threads.
    */
    @JvmStatic
      public val IO: CoroutineDispatcher = DefaultScheduler.IO
    

    四位兄弟分别是Default的使用JVM的共享线程池,Main的表示主线程,根据环境不同而不同。Unconfined没有指定的一个线程。IO表示缓解一些阻塞的IO线程同样使用的共享线程池。

    我们直接通过以下代码详细的看一下情况。

    launch { // 使用父协程的上下文, 也就是 main 函数中的 runBlocking 协程
          println("main runBlocking      : I'm working in thread ${Thread.currentThread().name}")
      }
      launch(Dispatchers.Unconfined) { // 非受限 -- 将会在主线程中执行
          println("Unconfined            : I'm working in thread ${Thread.currentThread().name}")
      }
      launch(Dispatchers.Default) { // 会被派发到 DefaultDispatcher
          println("Default               : I'm working in thread ${Thread.currentThread().name}")
      }
      launch(Dispatchers.IO) { // 会被派发到 DefaultDispatcher
          println("IO               : I'm working in thread ${Thread.currentThread().name}")
      }
      launch(Dispatchers.Main) { // 会被派发到 DefaultDispatcher
          println("Main              : I'm working in thread ${Thread.currentThread().name}")
      }
    

    输出结果是

     Unconfined            : I'm working in thread main
     Default               : I'm working in thread DefaultDispatcher-worker-1
     IO               : I'm working in thread DefaultDispatcher-worker-3
     Exception in thread "main" java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android'
    

    我们看到Main报错了,说需要提供Main dispatcher的依赖,也就是说这个会在andorid中使用到,BTW,为了方便,我这里是Intellij Idea环境,不是AS。所以我们基本可以理解这个Main指的就是android中的主线程。我们先把Main去掉,因为第一段什么都没有传的launch方法都没有打印出来。去掉报错的Main之后我们再看一遍。

    Unconfined            : I'm working in thread main
    Default               : I'm working in thread DefaultDispatcher-worker-1
    IO               : I'm working in thread DefaultDispatcher-worker-3
    main runBlocking      : I'm working in thread main
    

    我们先抛开顺序一个一个看,第一个launch直接用的默认的,由于是在runBlocking的代码块中调用的,所以直接是运行的runBlocking的上下文即主线程。

    Unconfined也在主线程,DefaultIO都是在DefaultDispatcher-worker-的工作线程中。