如何挂起电话“;“等等”;对于内部的阻塞线程,在Kotlin中?
让我来解释一下 我有一个暂停的功能如何挂起电话“;“等等”;对于内部的阻塞线程,在Kotlin中?,kotlin,coroutine,Kotlin,Coroutine,让我来解释一下 我有一个暂停的功能 suspend fun foo(){ if(startFlag){ myMethod() } } 当我第一次调用myMethod()时,我将检查startFlag的值,如果为false,将调用myMethod 我不能为此使用init{},必须是在我第一次调用foo()时(由于更复杂的逻辑) 我的问题是: 在我第一次调用foo()时,我将调用myMethod()(据说这需要很长时间才能解决) 然后又调用了foo(),我想检测myMeth
suspend fun foo(){
if(startFlag){
myMethod()
}
}
当我第一次调用myMethod()
时,我将检查startFlag
的值,如果为false,将调用myMethod
我不能为此使用init{}
,必须是在我第一次调用foo()
时(由于更复杂的逻辑)
我的问题是:
在我第一次调用foo()
时,我将调用myMethod()
(据说这需要很长时间才能解决)
然后又调用了foo()
,我想检测myMethod()
已经在线程中运行,我应该等待它
但是由于fun foo()
本身是挂起的,所以不确定我是否可以这样做
有什么想法吗?如果我理解正确,可以使用
互斥锁来实现,例如:
var startFlag = true
val mutex = Mutex()
suspend fun foo() = mutex.withLock {
if (startFlag) {
myMethod()
startFlag = false
}
}
这是一个有趣的问题,我想与大家分享一种解决方法,希望能有所帮助
碱性溶液
您可以使用延迟的恢复myMethod()
计算。基本上,代码可以如下所示:
val myMethodDeferred = GlobalScope.async {
myMethod()
}
suspend fun foo(){
myMethodDeferred.await()
}
val myMethodRetryable = Retryable { myMethod() }
suspend fun foo(){
myMethodRetryable.await()
}
async
函数返回一个Deferred
对象,该对象保存myMethod
的计算。调用myMethodDeferred.wait()
时,它将等待计算结束并返回myMethod
的结果(以便在需要时使用)
如果您希望仅在调用foo()
时执行计算,可以像这样向async
调用添加一个参数:async(start=CoroutineStart.LAZY){…
。这将导致计算在第一次调用时延迟启动。wait()
调用
(可以用任何CoroutineScope
或作为构造函数参数传递给类的作用域替换GlobalScope
。如果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()
}
}
现在,对foo()
的每次调用都将等待myMethod()
的计算完成,但是如果它已经以失败结束,它将重新运行它并等待新的计算
(这里,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个许可证的信号量
val myJobSemaphore = Semaphore(1)
您是否能够共享更多代码?例如,整个类?
val myJobSemaphore = Semaphore(1)