如果启动了一个协程,但没有指定调度程序(例如GlobalScope.launch {}
),则使用什么调度程序?
如果协同程序是在主线程中启动的,它会使用Dispatchers.main
吗?
此外,如果您没有在协同程序中指定调度器或上下文,会发生什么?假设您执行了数据库操作,但没有在任何位置指定Dispatchers.IO
。这会引起什么重大问题吗?
如果启动了一个协程,但没有指定调度程序(例如GlobalScope.slaunch{}),则使用什么调度程序?
如果在调用协程生成器(launch
、async
等)时没有指定调度程序,则可以从启动协程的CoroutineScope
中获取调度程序。如果该范围的上下文中没有调度程序,协程将使用Dispatchers.Default
(例如,请参阅启动文档)。
注意,作用域是协同程序生成器调用的接收器:
- 如果您看到
GlobalScope.launch { ... }
,那么GlobalScope
就是作用域 - 如果你看到
scope.launch { ... }
,看看那个scope
- 如果您看到没有接收器的
launch { .. }
,那么CoroutineScope
的某个实例在该代码中必须作为this
可用,这就是作用域(有关它可能来自哪里的示例,请参见下面)
以下是关于在最常见的协程作用域中使用的调度器的一些信息:
如果作用域是GlobalScope
,那么它就没有任何调度器,所以正如前面提到的,协程将使用Dispatchers.Default
。
如果作用域是Android提供的lifecycleScope
或viewModelScope
,则协同程序默认使用Dispatchers.Main.immediate
。
如果范围是用CoroutineScope()
工厂函数创建的,而没有特定的调度器,则将使用Dispatchers.Default
(请参阅文档中的"自定义用法"部分)。
如果作用域是用CoroutineScope(someContext)
工厂函数创建的,并且someContext
包含一个调度器,那么这个调度器将用于启动协程。
如果范围是使用MainScope()
创建的,并且没有特定的调度器,那么它将根据相同的文档使用Dispatchers.Main
。
如果作用域是用MainScope(someContext)
工厂函数创建的,并且someContext
包含一个调度器,那么这个调度器将用于启动协程。
如果作用域是由runBlocking
提供的,而没有特定的调度器,那么它将使用一个特殊的调度器,它的工作方式类似于事件循环,并在调用runBlocking
的线程中执行协同程序(这很好,因为该线程无论如何都会被阻塞)。
如果作用域由runBlocking(someContext) { ... }
提供,并且someContext
包含一个调度器,那么子协同程序将使用该调度器。
如果作用域是由另一个(外部)协程生成器(如launch
或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
启动协同程序,那么是的,它将在主线程上运行(在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上被阻塞时,其他协同程序将可以自由运行
您可以在文档中阅读更多关于如何使用调度器的信息。