上下文:
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
data class DatabaseData(
val a: String,
val b: String
)
interface DatabaseFetcher {
suspend fun get(): DatabaseData
}
class MyClass(
private val databaseFetcher: DatabaseFetcher
) {
suspend fun a() = coroutineScope {
val a = async { databaseFetcher.get().a }
//imagine more async{}'s here
a //imagine a gets computed in regards to the other async{}'s as well
}
suspend fun b() = databaseFetcher.get().b
}
class MyController(private val databaseFetcher: DatabaseFetcher) {
suspend fun someFun() = coroutineScope {
// the reduced example doesn't need a coroutineScope of course, imagine some async{} here
MyClass(databaseFetcher)
}
}如果a()或b()在MyClass上被调用,我只尝试调用MyClass一次。从根本上说,这是一个懒散的未来,在调用a()或b()时就开始了,但需要协同。
到目前为止,我已经尝试过:
by lazy{}作为coroutineScope问题,我也不能使用withContext(Dispatchers.IO),因为我使用自定义上下文(多线程、Spring请求作用域数据等)--在这里传递上下文似乎有些麻烦(这会不会是一种糟糕的做法?)在构造MyClass时,D16永远不会被await()>ed>ed> on,则会无限期阻塞,这可能发生在既不调用a()或D19时。它也无限期地阻塞,因为在构造coroutineScope时会构造相应的MyClass,这将在MyClass完全构建之后调用为a()和b(),因为(据我所知)只有在完成了所有的子程序时,coroutineScope才会被解除阻塞,对于在curent作用域之外等待的懒惰异步,使用更广泛的coroutine上下文可能会泄漏,而这是真的吗?我找不到很多关于的事
这是在GraphQL上下文中完成的,其中可以选择a、b或两者。对于这一点,有一些简单的解决方案,但由于我仍然在学习协同机制,我想知道是否有一个优雅的解决方案,我还没有看到。CoroutineStart.LAZY的问题真的让我大吃一惊:)
发布于 2022-05-20 22:41:48
我已经找到了解决办法:
fun <T : () -> U, U> T.memoized(): suspend () -> Deferred<U> {
val self = this
val deferred: CompletableDeferred<U> = CompletableDeferred()
val started = AtomicBoolean(false)
return suspend {
if (started.compareAndExchange(false, true)) {
deferred
} else {
coroutineScope {
async {
deferred.complete(self())
deferred.await()
}
}
}
}
}任何() -> T函数(基本上是任何带有捕获参数的函数)都可以是.memoized()。无论被调用者首先调用什么,返回的suspend fun都将用于启动Deferred<U>,同时允许所述被调用者在他认为合适的时候阻塞:
val expensive = { someExpensiveFun(param, param2 }.memoize();
withContext(Dispatchers.IO) { // or some other context
val a = expensive()
val b = expensive()
a.await()
b.await()
}https://stackoverflow.com/questions/72268944
复制相似问题