首发于公众号: DSGtalk1989
29.上下文与调度器
-
调度器与线程
前面我们说到
launch
提供了3个可选的参数,分别是上下文,启动模式和协程函数。其中的第一个上下文
CoroutineContext
,我们点开来详细的看一下。@SinceKotlin("1.3") public interface CoroutineContext
是个接口,我们继续看在这个文件的最下面,有这样一段代码,继续一个接口
Element
实现了协程上下文的接口。public interface Element : CoroutineContext
我们尝试着点击一下
Element
的find 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`,点开来,看到四个很熟悉的名字`Default`,`Main`,`Unconfined`,`IO`,我们尝试针对这四个名词的注释做一些理解。
```js
/**
* 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
也在主线程,Default
和IO
都是在DefaultDispatcher-worker-
的工作线程中。
Kotlin学习笔记之 13 基础操作符run、with、let、also、apply