1

我正在使用公开了回调函数的第三方库。回调函数将在成功时调用。回调函数不是挂起函数,但是当我尝试在非挂起函数内部进行调用以返回挂起函数的结果时,该函数使用 aysnc 和 await 进行 IO 调用,调用永远不会被强制执行。下面我想出了一个简单的代码片段来演示这个问题。

open class TestVerticle: CoroutineVerticle() {

  override suspend fun start() {

    awaitBlockingExample()

  }

 fun awaitBlockingExample():String {

    val future= async(vertx.dispatcher()) {

        makeSuspendFunCall()
     }
     val result:String= runBlocking(vertx.dispatcher()){future.await()}
     println(" The final Result is $result")
     return result
   }

  suspend fun makeSuspendFunCall():String{
    println("Comming here 3")
    delay(500)
    val resp="Test"
    return resp
  }

}
fun main(args: Array<String>) = runBlocking {
    Vertx.vertx().deployVerticle("TestVerticle")
}

如果我删除 makeSuspendFunCall 中的延迟函数,程序运行良好,但如果我添加延迟函数,它会挂起。我实际上是在这里使用延迟函数模拟挂起函数网络调用。在这种情况下,如何从 awaitBlockingExample 获得结果?我清楚地明白,通过将 awaitBlockingExample 作为挂起函数,我可以完成这项工作并删除异步并在内部运行阻塞调用。但是这里 awaitBlockingExample (非挂起函数)代表了一个由本方库提供的实现,它在我们的实现中被覆盖。比如guava缓存提供了reload函数,我想重写reload函数(非挂起函数),并从reload方法中调用协程函数来刷新数据库或网络调用的缓存值。

4

3 回答 3

3

问题是它vertx.dispatcher()使用单个线程作为事件循环并runBlocking阻止该线程。

细节:

您的awaitBlockingExample()函数正在此 Vertx 事件循环线程上运行,因为它是从suspend start()函数触发的。如果你调用runBlocking()这个 Vertx 线程被阻塞并且永远不会被释放。但是你的其他协程,例如async(),现在没有线程来完成他们的工作。

解决方案:

我假设awaitBlockingExamplefromstart函数的调用仅在此示例中发生。实际上,我会假设外部回调使用自己的线程。那么就完全没有问题了,因为现在外线程被阻塞了:

override suspend fun start() {

    //simulate own thread for external callback
    thread {
        awaitBlockingExample()
    }
}

fun awaitBlockingExample():String {

    val future= async(vertx.dispatcher()) {

        makeSuspendFunCall()
    }
    val result:String= runBlocking(vertx.dispatcher()){future.await()}
    println(" The final Result is $result")
    return result
}

BTW:你不需要async()块,你可以直接调用makeSuspendFunCall()fromrunBlocking()

fun awaitBlockingExample():String = runBlocking(vertx.dispatcher()){
    val result = makeSuspendFunCall()
    println(" The final Result is $result")
    result
}
于 2018-11-07T17:10:20.963 回答
0

对于 Kotlin 1.3.0 及更高版本

private val mainScope = CoroutineScope(Dispatchers.Main)

fun start(){
        mainScope.launch { 
            val data = withContext(Dispatchers.IO){
                //This function will return the result. Return type of the below function will be type of data variable above.
                awaitBlockingExample()
            }
            //use your data result from async call. Result will be available here as soon as awaitBlockingExample() return it.
        }
        //Your function will continue execution without waiting for async call to finish.
    }

 fun awaitBlockingExample():String {
    //Your Logic
   }

希望这会有所帮助。

于 2018-11-15T10:10:17.877 回答
0

Try next approach:

override fun start() {
    GlobalScope.launch {
        val result = awaitBlockingExample()
    }
}

suspend fun awaitBlockingExample(): String {
    val response =  makeSuspendFunCall()
    println(" The final Result is $response")
    return response
}

suspend fun makeSuspendFunCall():String{
    println("Comming here 3")
    return suspendCoroutine {
        delay(500)
        val resp="Test"
        it.resume(resp)
    }
}
于 2018-11-14T05:18:16.320 回答