让我更好地解释一下...
我具有暂停功能
suspend fun foo(){
if(startFlag){
myMethod()
}
}
当我第一次调用myMethod()
时,我将检查startFlag
的值,如果值为false,则会调用myMethod。
我不能为此使用init {}
,必须是我第一次调用foo()
时(由于逻辑更复杂)。
我的问题:
我第一次打电话给foo()
时会打电话给myMethod()
(让我们说要解决很长时间)
然后再次调用foo()
,我想检测到myMethod()
已经在线程中运行,我应该等待它。
但是由于fun foo()
本身已被暂停,因此不确定是否可以这样做。
有什么想法吗?
答案 0 :(得分:4)
这是一个有趣的问题,我想分享一种解决方法,希望这会有所帮助。
您可以使用myMethod()
恢复Deferred
计算。基本上,代码看起来像这样:
val myMethodDeferred = GlobalScope.async {
myMethod()
}
suspend fun foo(){
myMethodDeferred.await()
}
async
函数返回一个保存Deferred
计算的myMethod
对象。当您调用myMethodDeferred.await()
时,它将等待计算结束并返回myMethod
的结果(如果需要,请使用该结果)。
如果只希望在调用foo()
时执行计算,则可以像这样将参数添加到async
调用中:async(start = CoroutineStart.LAZY){ ...
。它将导致计算延迟从第一个.await()
调用开始。
({GlobalScope
可以替换为任何CoroutineScope
,或者替换为作为构造函数参数传递给类的范围。如果myMethod()
不是挂起函数而是阻塞函数如您所描述的,您可能希望使用适当的CoroutineScope,例如CoroutineScope(Dispatchers.IO)
(如果它执行I / O计算。)
该解决方案的缺点是myMethod()
失败并发生异常。如果myMethod()
失败,则该失败将存储在延迟中,并且对foo()
的每次调用也会失败,而不是尝试再次运行myMethod()
。
要处理myMethod()
的失败,我建议声明一个名称为Retryable
的类,该类将保存可以重试的计算,与Deferred
的保存方法类似。代码:
class Retryable<T>(private val computation: suspend CoroutineScope.() -> T) {
private var deferred: Deferred<T>? = null
suspend fun await(): T {
val oldDeferred = deferred
val needsRetry = oldDeferred == null || oldDeferred.isCompleted && oldDeferred.getCompletionExceptionOrNull() != null
if (needsRetry) {
deferred = GlobalScope.async(block = computation)
}
return deferred!!.await()
}
}
用法应如下所示:
val myMethodRetryable = Retryable { myMethod() }
suspend fun foo(){
myMethodRetryable.await()
}
现在,每次对foo()
的调用都将等待myMethod()
的计算完成,但是如果已经以失败结束,它将重新运行它并等待新的计算。 / p>
(这里,CoroutineScope
应该作为参数传递给Retryable
,但我不想使示例代码复杂化。)
由于myMethod()
仅应调用一次,因此,如果不能在init {}
上调用它,则可以在工厂函数上调用它。
示例代码:
class MyClass(private val myMethodResult: String) {
companion object {
suspend fun create(): MyClass {
val myMethodResult = myMethod()
return MyClass(myMethodResult)
}
private suspend fun myMethod(): String {
...
}
}
...
}
如果您可以在使用前从挂起函数初始化MyClass
一次,则很有用。
(如果myMethod()
不是挂起的函数,而是您所描述的阻塞函数,则可能需要用适当的上下文包装它,例如,withContext(Dispatchers.IO) { myMethod() }
如果进行I / O计算。)
答案 1 :(得分:1)
您可以只使用Semaphore
。
...
suspend fun foo(semaphore: Semaphore) = semaphore.withPermit {
if (startFlag) {
myMethod()
}
}
...
然后创建Semaphore
并以1个许可作为参数传递。
val myJobSemaphore = Semaphore(1)
答案 2 :(得分:-1)
如果我没看错,可以使用Mutex
来完成,例如:
var startFlag = true
val mutex = Mutex()
suspend fun foo() = mutex.withLock {
if (startFlag) {
myMethod()
startFlag = false
}
}
这是指向playground
的链接