如何在主线程执行结束后完全执行异步方法?

huangapple go评论65阅读模式
英文:

How to completely execute async method even after main thread execution ends?

问题

我在Kotlin中有一个需求,我需要在一个方法中包含一些逻辑,其中包括日志记录和网络调用。

我尝试了不同的协程构建器和async {},但都没有成功,而且我不能使用线程。将会有成千上万个请求。

这个逻辑的延迟比主流程本身还要高。我想异步执行这个方法。但问题是:

  • 即使执行是异步的,主线程仍然会等待它完成后才返回响应。
  • 如果我使用GlobalScope,则日志记录不会发生(我看不到日志,也不确定为什么会发生这种情况)。
@GRpcService
class myApi() {

   override suspend fun createSomething(request: Request): Response  = coroutineScope {
       
        getSomething()
        getSomethingSomething()
        
        // 我想在这里异步调用一个方法

        return response // 但这个响应不应该以任何方式延迟
    }
}
英文:

I have a requirement in Kotlin where I need some logic in a method which includes logging and a network call.

I have tried different coroutine builders and async {} but no luck and I can not use threads. There will be thousands of requests.

This logic has high latency than the main flow itself. I want to execute this method asynchronously. But problem is

  • Even the execution is async the main thread waits for it to complete
    before returning the response.
  • If I use GlobalScope, the logging does not happen (I do not see the logs and I am not sure why this happens).
@GRpcService
class myApi() {

   override suspend fun createSomething(request: Request): Response  = coroutineScope {
       
        getSomething()
        getSomethingSomething()
        
        // I want to call a method here asynchronously 

        return response // but this response should not be delayed in any way
    }
}

答案1

得分: 1

我有一个想法,但我不确定它是否有效,因为我还没有测试过,但你可以使用 NonCancellable 上下文。以下是示例:

@GRpcService
class myApi() {

   override suspend fun createSomething(request: Request): Response  = 
    coroutineScope {

    getSomething()
    getSomethingSomething()

    launch(NonCancellable + Dispatchers.IO) {
       // 我想在这里异步调用一个方法

     }
    return response // 但是这个响应不应该以任何方式被延迟
 }
}
英文:

I have an idea but I am not sure if it will work as I have not tested it but you can use NonCancellable context. Here is example:

 @GRpcService
 class myApi() {

   override suspend fun createSomething(request: Request): Response  = 
    coroutineScope {
   
    getSomething()
    getSomethingSomething()

    launch(NonCancellable + Dispatchers.IO) {
       // I want to call a method here asynchronously 

     }
    return response // but this response should not be delayed in any way
 }
}

答案2

得分: 1

如果我理解正确,您希望能够触发不必等待当前协程完成但必须在应用程序终止之前完成的异步工作。我尚未尝试过这样做,但也许您可以存储顶级 runBlocking 的范围并使用它来运行这些附加任务。

private lateinit var topScope: CoroutineScope

fun main() = runBlocking(Dispatchers.Default) {
    topScope = this
    //. . .
}

fun submitRequiredAsyncTask(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> Unit
): Job {
    return topScope.launch(context) { 
        try { block() }
        catch(e: Exception) {
            if (e is CancellationException) throw e
            // logging
        }
    }
}
@GRpcService
class myApi() {

   override suspend fun createSomething(request: Request): Response  = 
       coroutineScope {
   
           getSomething()
           getSomethingSomething()

           submitRequiredAsyncTask(Dispatchers.IO) {
               // 我想在这里异步调用一个方法
           }
           response // 但这个响应不应该以任何方式被延迟
       }
   }

}

我们在这里运行这些任务作为顶级 runBlocking 的子协程,它必须等待其所有子协程完成。您之前尝试将它们作为当前协程的子协程运行(导致当前协程等待其子协程,从而导致挂起),或者作为 GlobalScope 协程运行(主函数将不等待它们完成)。

英文:

If I understand correctly, you want to be able to fire off asynchronous work that doesn’t have to be waited for in the current coroutine, but must be finished before the app terminates. I have not tried this, but maybe you can store your top level runBlocking’s scope and use it to run these side tasks.

private lateinit var topScope: CoroutineScope

fun main() = runBlocking(Dispatchers.Default) {
    topScope = this
    //. . .
}

fun submitRequiredAsyncTask(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.()->Unit
): Job {
    return topScope.launch(context) { 
        try { block() }
        catch(e: Exception) {
            if (e is CancellationException) throw e
            // logging
        }
    }
}
@GRpcService
class myApi() {

   override suspend fun createSomething(request: Request): Response  = 
       coroutineScope {
   
           getSomething()
           getSomethingSomething()

           submitRequiredAsyncTask(Dispatchers.IO) {
               // I want to call a method here asynchronously 

           }
           response // but this response should not be delayed in any way
       }
   }

}

What we are doing here is running these tasks in child coroutines of the top level runBlocking, which must wait for all its children to finish. You were trying to run them either as children of the current coroutine (causing the hang as the current coroutine waits for its children) or as GlobalScope coroutines (the main function won’t wait for them to finish).

huangapple
  • 本文由 发表于 2023年7月3日 18:48:27
  • 转载请务必保留本文链接:https://go.coder-hub.com/76604021.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定