Kotlin协同程序`runBlocking`

Ang*_*ina 10 android kotlin kotlin-coroutines

我正在学习Kotlin协同程序.我读过这runBlocking是桥接同步和异步代码的方法.但是如果runBlocking停止UI线程,性能提升是多少?例如,我需要在Android中查询数据库:

    val result: Int
    get() = runBlocking { queryDatabase().await() }

private fun queryDatabase(): Deferred<Int> {
    return async {
        var cursor: Cursor? = null
        var queryResult: Int = 0
        val sqlQuery = "SELECT COUNT(ID) FROM TABLE..."
        try {
            cursor = getHelper().readableDatabase.query(sqlQuery)
            cursor?.moveToFirst()
            queryResult = cursor?.getInt(0) ?: 0
        } catch (e: Exception) {
            Log.e(TAG, e.localizedMessage)
        } finally {
            cursor?.close()
        }
        return@async queryResult
    }
}
Run Code Online (Sandbox Code Playgroud)

查询数据库会停止主线程,所以它似乎需要与同步代码相同的时间?如果我错过了什么,请纠正我.

Mar*_*nik 25

runBlocking 是桥接同步和异步代码的方法

我一直碰到这句话,这是非常误导的.

runBlocking几乎从来没有在生产中使用的工具.它解除了协同程序的异步,非阻塞性​​质.如果你碰巧已经有一些基于协同程序的代码,你想在协同程序没有提供任何价值的上下文中使用它,你可以使用它:阻塞调用.一个典型的用途是JUnit测试,其中测试方法必须坐下并等待协程完成.

您也可以在main方法中使用协同程序时使用它.

滥用runBlocking已经变得如此普遍,以至于Kotlin团队实际上试图添加一个快速失败检查,如果你在UI线程上调用它会立即崩溃你的代码.当他们这样做时,已经打破了这么多代码,他们不得不删除它.

  • 应该使用什么来代替 runBlocking 来无阻塞地调用协程? (4认同)
  • 对runBlocking的滥用已经变得非常普遍,以至于Kotlin团队实际上不得不通过快速失败来恢复更改:) (3认同)
  • 这种对快速失败的解释实际上是误导性的,这不仅仅是误用的问题(确实存在),问题是存在有效的用例,例如应用程序启动、资源清理等,您可以在协程上看到对此的讨论问题跟踪器,带有附加参数“是的,阻止,我知道我在做什么,请不要崩溃”的解决方案在我看来不会更好。另一个问题是它在发布代码时失败,但在调试时失败,这是一种错误的策略,会导致错误,而不是阻止错误 (2认同)
  • @Trevor 你应该“启动”一个协程。 (2认同)
  • @user3410835 `runBlocking` 返回它启动的协程的结果,这意味着它必须等待它完成。 (2认同)
  • 如果我正在与已启动工作线程的旧式代码集成,并且我想在该线程中调用“挂起”函数,并且我希望得到以下结果,那么使用“runBlocking”是否合适?同步运行(所以启动和异步不合适)?我可以从工作线程中调用“runBlocking”吗?据我看来,这只是有点浪费线程,但这还不错,不是吗? (2认同)

Rol*_*and 9

实际上,您runBlocking曾经使用“阻塞”代码来调用挂起函数,否则这些代码将无法在那里调用或换句话说:您使用它来调用suspend协程上下文之外的函数(在您的示例中,传递给asyncsuspend函数的块就是该函数)。同样(更明显的是,顾名思义,顾名思义),该调用也是阻塞调用。因此,在您的示例中,它好像没有async到位一样被执行。它等待(可中断地阻塞),直到runBlocking-block中的所有内容完成为止。

例如,假设您的库中的函数如下:

suspend fun demo() : Any = TODO()
Run Code Online (Sandbox Code Playgroud)

该方法无法从调用main。对于这种情况,您可以使用runBlocking例如:

fun main(args: Array<String>) {
  // demo() // this alone wouldn't compile... Error:() Kotlin: Suspend function 'demo' should be called only from a coroutine or another suspend function
  // whereas the following works as intended:
  runBlocking {
    demo()
  } // it also waits until demo()-call is finished which wouldn't happen if you use launch
}
Run Code Online (Sandbox Code Playgroud)

关于性能提升:实际上,您的应用程序可能宁愿响应速度更快,而不是性能更高(有时性能也更高,例如,如果您有多个并行操作而不是多个顺序操作)。但是,在您的示例中,分配变量时您已经阻塞了,因此我想说您的应用程序还没有变得更加响应。您可能希望异步调用查询,然后在响应可用后立即更新UI。因此,您基本上只是省略runBlocking而宁愿使用类似的东西launch。您可能也对使用协程进行UI编程指南感兴趣。