Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/kotlin/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何挂起电话“;“等等”;对于内部的阻塞线程,在Kotlin中?_Kotlin_Coroutine - Fatal编程技术网

如何挂起电话“;“等等”;对于内部的阻塞线程,在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)