可以使用冗余/嵌套的 withContext 调用吗?

gar*_*ple 6 kotlin kotlin-coroutines

我有一个用 Kotlin 编写的个人项目,并且养成了非常慷慨地使用的习惯withContext(...)。我倾向于withContext(Dispatchers.IO)在调用任何可能与 I/O 相关的内容时使用。

例如:

suspend fun getSomethingFromDatabase(db: AppDatabase) = withContext(Dispatchers.IO) {
    return // ...
}

suspend fun doSomethingWithDatabaseItem(db: AppDatabase) {
    val item = withContext(Dispatchers.IO) {
        getSomethingFromDatabase(db)
    }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

withContext(Dispatchers.IO)您可以在第二个函数中看到冗余。我在这里格外谨慎,因为我可能不知道/记得是否getSomethingFromDatabase切换到适当的上下文。这会影响性能吗?这很糟糕吗?惯用的处理方式是什么Dispatchers

注意:我知道以这种方式在不同的上下文之间切换是完全可以的,但是这个问题具体是关于使用相同的上下文

Ten*_*r04 7

withContext除了调用需要特定上下文的代码之外,您不需要任何其他东西。因此,withContext(Dispatchers.Main)仅当您使用需要主线程的 UI 函数时才应使用。withContext(Dispatchers.IO)并且您应该仅在调用阻塞IO 相关代码时使用。

正确的挂起函数不会阻塞(请参阅此处的挂起约定部分),因此,您不必指定调度程序来调用挂起函数。

例外情况:

  • 您正在使用其他人的代码或 API,而他们错误地使用了挂起函数。
  • 关于需要主线程的函数并没有真正的约定。它们不会阻塞,但如果不调用 Dispatchers.Main 则可能会中断。IMO,如果它是公共功能,则将任何必要的withContext封装内部化,而不是暴露失败机会。
  • 也许在私有函数中,您想要优化某些内容并避免不必要的调度程序切换,因此您可以相应地仔细构建代码。我很难想象这不是一个愚蠢的微优化的情况——如果您在某些函数内切换上下文(通过在函数suspend内调用suspend函数),那么通过避免在此处切换上下文并不会节省太多时间那里。我更喜欢从源头解决问题。封装的好处不仅适用于类,也适用于单个函数。

我不知道您的 AppDatabase 类是什么,但如果它设计合理,它将公开挂起函数而不是阻塞函数,因此您不需要withContext从中检索值。但是,如果它确实公开了用于检索项目的阻塞函数,那么您的第一个函数的代码是正确的。

你的第二个函数肯定不需要,withContext因为它只是用它来调用我可以看到的挂起函数。

至于是否可以使用冗余上下文切换……除了可能浪费一点时间和内存上下文切换以及无缘无故地分配 lambda 之外,它不会造成任何损害。而且它会降低你的代码的可读性。