尝试调用运行阻塞时,Kotlin Vertex与协程阻塞

尝试调用运行阻塞时,Kotlin Vertex与协程阻塞,kotlin,vert.x,kotlin-coroutines,vertx-verticle,Kotlin,Vert.x,Kotlin Coroutines,Vertx Verticle,我正在使用一个第三方库,它公开了一个回调函数。成功后将调用回调函数。回调函数不是挂起函数,但当我尝试在非挂起函数中进行调用以返回挂起函数的结果时(该函数使用aysnc和WAIT进行IO调用),调用永远不会被强制执行。下面我给出了一个简单的代码片段来演示这个问题 open class TestVerticle: CoroutineVerticle() { override suspend fun start() { awaitBlockingExample() } fun

我正在使用一个第三方库,它公开了一个回调函数。成功后将调用回调函数。回调函数不是挂起函数,但当我尝试在非挂起函数中进行调用以返回挂起函数的结果时(该函数使用aysnc和WAIT进行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")
}
开放类TestVerticle:CoroutineVerticle(){
覆盖暂停乐趣开始(){
waitingblockingexample()
}
示例():字符串{
val future=async(vertx.dispatcher()){
makeSuspendFunCall()
}
val结果:String=runBlocking(vertx.dispatcher()){future.await()}
println(“最终结果为$Result”)
返回结果
}
suspend fun makeSuspendFunCall():字符串{
println(“此处提交3”)
延迟(500)
val resp=“测试”
返回响应
}
}
fun main(args:Array)=运行阻塞{
Vertx.Vertx().deployVerticle(“TestVerticle”)
}

如果我删除makeSuspendFunCall中的延迟函数,程序将运行,但如果我添加延迟函数,程序将挂起。我实际上是在用延迟函数模拟挂起函数网络调用。在这种情况下,如何从WaitingBlockingExample中获得结果?我清楚地理解,通过将waitingblockingexample作为suspend函数,我可以实现这一点,并删除其中的异步和运行阻塞调用。但在这里,WaitingBlockingExample(非挂起函数)表示一个由this party库提供的实现,它在我们的实现中被重写。例如,guava cache提供了一个重载函数,我想重写重载函数(非挂起函数)并从重载方法调用一个协程函数,以从数据库或网络调用中刷新缓存值。

问题在于
vertx.dispatcher()
使用单个线程作为事件循环,并
运行阻塞
阻塞此线程

详情:

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

解决方案:

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

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
}
顺便说一句:您不需要
async()
块,您可以从
runBlocking()

尝试下一种方法:

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)
    }
}

适用于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
   }

希望这会有所帮助。

正如我在问题中提到的,我特别想寻找一种解决方案,解决如何从非挂起函数调用协程IO阻塞调用的问题。我只是在代码片段中简化了我的用例。例如,guava cache提供了一个重载函数,我想重写重载函数(非挂起函数)并从重载方法调用一个协程函数,以从数据库或网络调用中刷新缓存值。我们也不能使用SuspendCorroutine,因为重载方法不是suspend函数,在SuspendCorroutine内部,我们不能调用Corroutine函数。从未挂起的函数调用挂起的函数总是阻塞的。您可以使用
runBlocking
。@sedovav如果您看到我在问题中使用run blocking,那么当我从非挂起函数调用挂起函数并调用挂起函数时,我的问题是调用另一个挂起函数(在问题延迟函数中),整个线程都会被阻塞,应用程序永远不会recovers@DevLoper我已经改变了我的答案,希望它现在更好。@Rene现在的答案是解决我讨论的问题,但它并没有完全解决它。如果我使用您的解决方案,我会在线程“main”io.vertx.core.VertxException中遇到以下错误异常:从错误的线程执行。找不到ForkJoinPool.commonPool-worker-1的上下文。这是因为suspend函数使用vertx提供的httpclient,而默认dispatcher没有vertx上下文。您使用的是哪个kotlin版本?
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
   }