kotlin - 使用协程实现后应用程序变慢
问题描述
我已将所有数据库表 greenDao 迁移到 Room 并带有挂起功能应用程序速度和性能在 CoroutineScope 调用函数时变慢。
withContext(Dispatchers.IO) 在 coroutineScope(Dispatcher.Main) 中运行非常慢,而 runBlocking 在没有协程的情况下运行很快
// This function running fast
fun getValFromDb() {
var objectOne: MyVal1 = runBlocking{ fetchFromTabelOne() }
var objectTwo: MyVal2 = runBlocking{ fetchFromTableTwoById(objectOne.getId) }
UpdateUi(objectTwo)
}
// This function running slow
fun getValFromDb() {
CoroutineScope(Dispatchers.Main).launch{
var objectOne: MyVal = withContext(Dispatchers.IO){ fetchFromDb() }
var objectTwo: MyVal2 = withContext(Dispatchers.IO){ fetchFromTableTwoById(objectOne.getId) }
UpdateUi(objectTwo)
}
}
注意:我们没有找到任何解决方案或建议在生产环境中实施 runBlocking,但与 runBlocking 相比,CoroutineScope 一直在减慢应用程序的速度
解决方案
编辑:这个答案假设您控制这里的大部分代码。评论中提供的代码堆链接显示有问题的函数是一个覆盖,父类可能不受 OP 的控制。如果此方法希望您Cursor
同步使用数据,则最好坚持使用runBlocking
(除非在主线程上调用它)。
原始答案
新代码有 3 个主要问题:
withContext(Dispatchers.IO)
强制切换到另一个线程池(因为协程的当前调度程序是Main
),所以它会减慢一点速度。此外,Room 在后台使用自己的调度程序来实现其挂起功能,因此它也会在那里切换上下文。这withContext(IO)
不仅是多余的,而且是有害的,因为它强制至少 2 次上下文切换。launch
不等价于runBlocking
,因为执行变成异步的。runBlocking
仅当其主体内的所有内容都完成时才返回,包括启动的协程等。但是,现在您使用launch
,对 的调用launch
几乎会立即返回,并且您无法保证继续前进时主体会完成。这是否可行实际上取决于您的函数的调用者,更具体地说,调用者在调用后做了什么(它是否依赖于正在完成的操作?)。这会极大地影响您的应用程序的行为,而不仅仅是它的性能(并且性能下降可能是行为变化的副作用)。CoroutineScope()
是一个创建新协程作用域的工厂函数。创建自定义范围是可以的,但应该小心。协程作用域的重点是控制在其中启动的协程的生命周期。通常建议在具有生命周期的组件中创建协程作用域,并在组件关闭/销毁/处置时取消该作用域。当您在 OP 的代码中创建一个范围时,您不会保留该范围的句柄,并且cancel()
在适当的时候不能处理,从而有效地泄漏协程。这就是结构化并发应该防止的。
问题 #1 的解决方案就是移除withContext
包装器。你不应该需要它们。
对于问题 #2,我无法真正回答这个问题,因为它取决于上下文,而且这里没有足够的信息。
对于问题 #3,您有多种选择。您可以做的一些示例:
制作你的函数
suspend
,而不是创建一个范围和launch
-ing 一个协程。这意味着您将创建协程的责任移到调用堆栈上,允许调用者选择范围。如果这样做,请确保将 UI 更新包装在其中,withContext(Dispatchers.Main)
以便它在主线程中运行,而不管调用者的当前上下文如何通过将您的函数定义为扩展
CoroutineScope
(或将其作为参数),使调用者将协程范围传递给您的函数。再次确保您在主线程中更新您的 UI,就像在前一点一样(因为您无法知道此时您将在哪个调度程序中运行)。这可以通过将调度程序传递给launch
或通过使用withContext
UI 更新来完成,就像前一点一样。创建一个自定义范围作为运行此代码的类的属性,但在这种情况下,您需要确保您的类具有某种生命周期,并且当您的类生命周期结束时该范围被取消。
推荐阅读
- php - JSEncrypt 可以签名和验证,但 PHP openssl_verify 失败
- r - 使函数在 R 中的赋值运算符的 LHS 上工作
- javascript - 与 if else 对应物相比,Javascript 三元组在 React 状态下产生意外结果
- html - 网页在移动设备上打开放大
- arrays - 使用 Google 表格的 2 组值中的共同值
- eclipse - 为什么 Eclipse 在应该使用 JUnit4 时却尝试使用 JUnit5?
- c# - Hangfire ContinueWithJob 卡在等待状态,尽管父作业已成功
- c - 以下 C 代码显示:格式“%c”需要“int”类型的参数,但参数 3 的类型为 char*
- php - PHP Closures - 获取闭包范围起源的类名
- javascript - 在 Bootstrap Datetimepicker 上禁用特定日期的特定时间