声明暂停功能时传达预期的线程类型(IO,默认,主线程)

时间:2018-10-26 02:25:33

标签: kotlin kotlinx.coroutines

在设计带有suspend函数的API时,有时候我想传达这个函数应该被调用,例如IO线程。其他时候这样做是 essential

通常看起来很明显;例如,应该使用Dispatchers.IO来调用数据库调用,但是如果它是一个接口函数,则调用者无法假定这一点。

这里最好的方法是什么?

2 个答案:

答案 0 :(得分:7)

如果suspend函数确实必须在特定的上下文中运行,请直接在函数主体中声明它。

suspend fun doInIO() = withContext(Dispatchers.IO) {

}

如果调用者应该能够更改调度程序,则该函数可以将调度程序添加为默认参数。

suspend fun doInIO(context: CoroutineContext = Dispatchers.IO) = withContext(context) {

}

答案 1 :(得分:2)

对于这样的合同,没有严格的机制,因此您可以灵活选择适合您和您的团队的机制。

1)始终使用withContext(Dispatcher.IO)。这既严格又高效,如果从IO上下文中调用方法,它将是fast-path'ed

2)基于命名/注释的约定。您可以在团队中达成协议,以IO调用任何以Dispatchers.IO结尾或具有特定注释的方法。这种方法主要适用于小型团队,并且仅适用于项目专用API 。一旦开始将其导出为其他团队的库/模块,此类合同就容易被破坏。

3)您可以将以前的方法与验证混合使用:

suspend fun readFile(file: ...) {
    require(coroutineContext[ContinuationInterceptor] == Dispatcher.IO) {
      "Expected IO dispatcher, but has ${coroutineContext[ContinuationInterceptor]} instead"
    }
    // read file
}

但是,只有当您没有将IO调度程序包装在某种委托/代理中时,此验证才有效。在这种情况下,您应该使验证了解此类代理,例如:

fun validateIoDispatcher(dispatcher: ContinuationInterceptor) {
    if (dispatcher is Dispatchers.IO) return
    if (dispatcher is ProjectSpecificIoDispatcher) return
    if (dispatcher is ProjectSpecificWrapperDispatcher) {
        validateIoDispatcher(dispatcher.delegate)
    } else {
        error("Expected IO dispatcher, but has $dispatcher")
    }
}
  

我想传达的是,应该在IO线程上调用此函数。在其他时候,这样做很重要。

不确定“应该”和“基本”之间有什么区别,但是请牢记这些方法,可以将其与默认方法参数(例如suspend fun probablyIO(dispatcher: CoroutineDispatcher = Dispatchers.IO)或更灵活的命名/注释约定)组合。