android - 在 Kotlin 协程中指定调度程序/上下文有多重要?如果你不指定它们会发生什么?
问题描述
如果启动协程但未指定调度程序(例如GlobalScope.launch {}
),使用什么调度程序?
如果该协程在主线程中启动,它会使用Dispatchers.main
吗?
另外,如果您没有在协程中指定调度程序或上下文会发生什么?假设您进行了数据库操作,但没有指定Dispatchers.IO
任何地方。这会导致任何重大问题吗?
解决方案
如果启动了协程但没有指定调度程序(例如 GlobalScope.launch {}),使用什么调度程序?
如果在协程构建器中没有指定调度器,你会从CoroutineScope
你启动协程的地方获取调度器。如果在该范围的上下文中没有调度器,协程将使用Dispatchers.Default
(例如,参见启动文档)。
请注意,范围是协程构建器调用的接收者:
- 如果你看到
GlobalScope.launch { ... }
那GlobalScope
是范围 - 如果你看到
scope.launch { ... }
了,看看那个scope
- 如果您
launch { .. }
在野外看到,则该代码段中的某些实例CoroutineScope
必须可用this
,因此这就是父作用域(有关其来源的示例,请参见下文)
以下是有关最常见协程作用域中使用的调度程序的一些信息:
如果作用域是GlobalScope
,那么它没有任何调度程序,所以如前所述协程将使用Dispatchers.Default
.
如果范围由 Android 提供lifecycleScope
或viewModelScope
由 Android 提供,则Dispatchers.Main.immediate
默认使用协程。
如果范围是使用CoroutineScope()
没有特定调度程序的工厂函数创建的,Dispatchers.Default
则将使用(请参阅文档中的“自定义用法”部分)。
如果使用MainScope()
和不使用特定调度程序创建范围,它将Dispatchers.Main
按照相同的文档使用。
如果范围由 提供runBlocking
,那么它将使用一个特殊的调度程序,它像一个事件循环一样工作,并在调用的线程中执行你的协程runBlocking
(这很好,因为该线程无论如何都会被阻塞)。
如果范围由另一个(外部)协程构建器提供,例如launch
or async
(这意味着您的协程是该协程的子协程),则调度程序将从用于启动父协程的范围中获取,除非它们覆盖它。因此,您可以一直向上,直到达到上述选项之一:
parentScope.launch {
// here, this = child scope that gets the dispatcher from parentScope
launch {
// inherits from parent launch
}
launch(Dispatchers.IO) {
// here, this = child scope with overridden dispatcher
launch {
// inherits Dispatchers.IO from parent launch's scope
}
}
}
如果该协程在主线程中启动,它会使用 Dispatchers.main 吗?
您可能可以将其与上一段拼凑在一起,但只是重申一下:这取决于您启动协程的范围。
如果您从lifecycleScope
/启动协程viewModelScope
,那么是的,它将在主线程(in Dispatchers.Main.immediate
)上运行。
如果你runBlocking
从主线程调用,那么它也会在主线程上运行协程(但不是在Dispatchers.Main
),但你绝对不应该在 Android 中这样做。
如果您使用类似的东西,MainScope()
它确实会在Dispatcher.Main
.
但在其他情况下,它很有可能继续运行Dispatchers.Default
- 检查范围!
另外,如果您没有在协程中指定调度程序或上下文会发生什么?假设您进行了数据库操作,但没有指定
Dispatchers.IO
任何地方。这会导致任何重大问题吗?
正如您从这个答案的开头所看到的,使用的调度程序取决于您的协程是如何启动的。如果我们假设您没有在任何地方指定任何调度程序,那么您的协程很有可能在Dispatchers.Main
(在 Android 中)或Dispatchers.Default
.
Dispatchers.Main
对 IO 的东西来说真的很糟糕,因为它会在 IO 发生时冻结你的 UI。我相信如果你在这个调度程序中运行错误的东西,Android 可能会崩溃,但我有一段时间没有做过 Android 开发,所以我不能肯定。
Dispatchers.Default
是一个共享线程池,其大小取决于执行代码的机器的内核数量,因此它适用于 CPU 密集型任务。如果您在此调度程序中启动一堆执行阻塞 IO 的协程,您可能会阻塞所有线程并阻止其他协程运行,这确实不理想 - 它可能会导致延迟或缓慢,尤其是在您非常依赖协程的情况下。
Dispatchers.IO
这并不神奇,但如果太多线程被阻塞,它会根据需要生成更多线程,以便其他协程可以运行。您仍然会产生额外线程的额外内存,但是当一些线程在 IO 上被阻塞时,其他协程将可以自由运行。
您可以在文档中阅读有关如何使用调度程序的更多信息。
推荐阅读
- c++ - 如何使自定义小部件内的子小部件的信号可连接?
- python - sqlalchemy删除功能问题,我想删除单个项目,但它实际上删除了整个类别?
- angular - 如何在角度 2 及以上的点击事件后切换 li 上的类
- entity-framework - EF Core:更新数据时防止个别查询
- php - 如何使用内容类型从 webhook 获取数据:application/x-www-form-urlencoded;charset=UTF-8?
- sql - 给定任何节点作为输入的 Oracle SQL 完整层次结构
- python - 如何使美元符号出现在python中的总和旁边?
- c# - 如何在文本框中迭代 Unicode?
- javascript - 使用正则表达式将每个数字实例包装在字符串中
- c# - 使用 Dapper 填充类对象