英文:
Kotlin Coroutine equivalent way as @Async in Spring Java
问题
Spring提供了使用@Async
和@EnableAsync
注解来支持异步执行的方式。在下面的Java代码中,使用这些注解后,我的API不会被阻塞,并且可以在昂贵的操作完成之前响应。
suspend fun saveUserDetail(userDetail: UserDetail): Response {
println("saveUserDetail Started...")
// very expensive operation
userRepo.save(userDetail)
println("saveUserDetail Completed...")
return response // replace 'response' with the actual response object
}
suspend fun save(userDetail: UserDetail) {
// took 30s
delay(30000)
}
请注意,上述Kotlin代码使用了suspend
关键字来标记可以挂起的函数,同时使用了delay
函数来模拟异步操作的等待时间。
英文:
Spring provides a way to support asynchronous execution with annotation of @Async
and @EnableAsync
, in the below code as Java, my API won't be blocked and can respond without waiting for the expensive operation to be finished.
@Post
public Response saveUserDetail(UserDetail userDetail) {
System.out.println("saveUserDetail Started...");
// very expensive operation
userRepo.save(userDetail);
System.out.println("saveUserDetail Completed...");
}
@Async
public void save(UserDetail userDetail) {
// took 30s
Thread.sleep(30000);
}
Now Kotlin has a coroutine package to write an async process in a sync way, however, what'd be the Kotlin way to achieve the above behaviour without using @Async
? I've tried suspend
and async
but they seem will still block my request thread that I cannot get my API response without waiting in expensive operation
答案1
得分: 1
Kotlin协程与常规Java线程不同。协程具有两个与线程不同的特性,我认为与您相关:
-
协程使用结构化并发。外部协程范围不会完成,直到所有子协程完成为止。
-
协程不应该被诸如
Thread.sleep()
等调用阻塞,除非采取谨慎的措施在专为此设计的调度程序中运行此类阻塞代码,例如Dispatchers.IO
。而是使用类似delay()
的挂起调用。有关详细信息,请参阅https://stackoverflow.com/a/76009163/430128。
现在在您的情况下,似乎您希望saveUserDetail
在不等待save
完成的情况下完成。请记住,这段Spring代码不安全,因为如果saveUserDetail
抛出错误,客户端将永远不会知道。或者如果由于某种原因saveUserDetail
永远不返回,线程将泄漏。 Kotlin中的结构化并发旨在通过默认方式防止这种不安全的代码。使用协程和结构化并发,响应将不会在saveUserDetail
调用完成之前返回,而且无论是否使用async
或launch
协程构建器,都没有关系,因为默认情况下它们只是启动一个子协程。父协程仍然不会在由async
或launch
启动的子协程完成之前完成。
如果您真的想要这段不安全的代码版本,您需要将协程启动到不是请求范围子协程的不同协程范围中,例如GlobalScope
。例如:
GlobalScope.launch {
userRepo.save(userDetail);
}
确保阅读文档中使用GlobalScope
的所有注意事项。
您还可以创建自己的后台范围并使用它。示例:
val backgroundOpsScope = CoroutineScope(SupervisorJob())
...
suspend fun saveUserDetail(userDetail: UserDetail) {
backgroundOpsScope.launch {
...昂贵的操作...
}
}
英文:
Kotlin coroutines are not the same as regular Java threads. Coroutines have two properties that are different than threads which I believe are relevant to you:
-
Coroutines use structured concurrency. An outer coroutine scope will not complete until all child coroutines are complete.
-
Coroutines should not be blocked by calls such as
Thread.sleep()
unless care is taken to run such blocking code in a dispatcher designed for it, such asDispatchers.IO
. Instead, use a suspending call likedelay()
See https://stackoverflow.com/a/76009163/430128 for details about this.
Now in your case, it appears you want saveUserDetail
to complete without waiting for save
to finish. Keep in mind this Spring code isn't safe, because if saveUserDetail
throws an error, the client will never know about it. Or if saveUserDetail
never returns for some reason, the thread will leak. Structured concurrency in Kotlin is intended to prevent such unsafe code by default. With coroutines and structured concurrency, the response will not return until the saveUserDetail
call finishes, and it doesn't matter if the async
or launch
coroutine builders are used or not, because by default they simply start a child coroutine. The parent still won't complete until the child coroutine started by async
or launch
is done.
If you really want the unsafe version of this code, you need to launch the coroutine into a different coroutine scope that is not a child of the request scope, such as the GlobalScope
. For example:
GlobalScope.launch {
userRepo.save(userDetail);
}
Make sure you read all the caveats of using GlobalScope
in the docs.
You can also create your own background scope and use that. Example:
val backgroundOpsScope = CoroutineScope(SupervisorJob())
...
suspend fun saveUserDetail(userDetail: UserDetail) {
backgroundOpsScope.launch {
... expensive operation ...
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论