声明挂起函数时传达预期的线程类型(IO、默认、主要)

Convey intended thread type (IO, default, main) when declaring suspend function

在设计一个带有 suspend 函数的 API 时,有时我想表达的是这个函数 应该 被调用,比如说,一个 IO 线程.其他时候必须这样做。

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

这里最好的方法是什么?

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

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

}

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

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

}

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

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

2) Naming/annotation-based 惯例。您可以在团队中达成协议,任何以 IO 结尾或具有特定注释的方法都应使用 Dispatchers.IO 调用。这种方法主要适用于小型团队,仅适用于项目私有 API。一旦您开始将其导出为 library/module 供其他团队使用,此类合同往往会被打破。

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

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

但此验证仅在您未将 IO 调度程序包装在某种 delegate/proxy 中时才有效。在这种情况下,您应该让验证意识到此类代理,例如:

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")
    }
}

I want to convey that this function should be called on, say, an IO thread. Other times that it is essential to do so.

不确定 "should" 和 "essential" 之间的区别,但是考虑到这些方法,您可以将它与默认方法参数结合使用,例如 suspend fun probablyIO(dispatcher: CoroutineDispatcher = Dispatchers.IO) 或更灵活的 naming/annotation约定。