跳至主要內容

协程调度器类型

guodongAndroid大约 3 分钟

前言

Kotlin 提供了一种使用 Dispatchers 在协程中线程通信的简洁方式。它们都是 CoroutineContext.Element 的具体实现,同时也是构成处理协程执行时神秘行为的一部分。在一般计算中,调度器是一个模块,它将 CPU 的控制权交给调度机制以选择哪个进程来执行。因此,调度器决定哪个进程是下一个需要 CPU 功率的进程,然后它会将该进程传递给调度器,以允许该进程耗尽实际资源。这两个模块或机制一起控制操作系统中的进程。

CoroutineDispatchers 与此类似。它们通过将协程委托给线程或线程池来决定协程如何使用可用资源。一旦你将某个调度器附加到协程,它就会被分配给调度器知道的线程或线程池。

由于它们处理线程,协程中的调度器可以 受限制无限制。受限制的调度器总是依赖预定义的系统上下文——比如 Dispatchers.Main。无论你使用多少次 Main 调度器,它总是将协程工作在主线程。另一方面,无限制的调度器,它们没有明确的操作上下文,也不遵循任何严格的规则。它们要么创建新线程来运行协程,要么在调用代码的线程执行工作,这使得它们 无法预测

下面有几个预定义的调度器,它们是:

  • Dispatchers.Default:启动协程的默认线程策略,受限于父上下文,通常是线程池。
  • Dispatchers.IO:与 Default 类似,它基于 JVM,并由线程池支持以减轻与 IO 相关的任务。
  • Dispatchers.Main:主线程调度器,连接到操作 UI 对象的线程。
  • Dispatchers.Unconfined:顾名思义,它是无限制的,它将在当前使用它的任何线程上运行。

接下来我们将一一分析它们,看看能用它们做什么。

默认调度器

默认调度器的名字已经道出它的身份。它用于协程的基础中,并在你不指定调度器时使用。它使用起来很方便,因为它由工作线程池支持,并且默认调度器可以处理的任务数量始终等于系统拥有的核心数量,并且至少是两个。由于整个线程机制和线程池是预先构建的,因此你可以依靠它进行与要脱离主线程的协程和操作相关的日常工作。

IO 调度器

同样的,IO 调度器的名字也说明了很多。无论何时,当你尝试处理输入和输出相关的事情时(比如:上传或者解密/加密文件),你可以使用这个调度器来让你更轻松的完成。话虽如此,但是它基于 JVM,所以如果你使用 Kotlin/JavaScript 或者 Kotlin/Native 项目,你将不能使用这个调度器。

Main 调度器

此调度器与具有某种形式用户界面的系统绑定,例如 Android 或可视化 Java 应用程序。如前所述,它将工作调度到处理 UI 对象的线程。如果没有 UI,你无法使用它,如果你尝试在不使用 SwingJavaFX 或不是 Android 应用程序的项目中调用 Dispatchers.Main,那么你的代码将崩溃。

最好在获取所需数据后在另一个协程中使用它,然后在显示之前处理所有逻辑。你只需将数据发布回主线程,然后进行渲染。或者更好的是,你可以在主线程上运行协程,然后使用 withContextasync/await 切换到后台,最后将结果拉回主线程进行渲染。