Spring提供了一种支持异步执行的方式,注解
@Async
和@EnableAsync
,在下面的Java代码中,我的API不会被阻塞并且可以响应而无需等待昂贵的操作完成。
@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);
}
现在 Kotlin 有一个协程包来以同步方式编写异步过程,但是,如果不使用
@Async
,Kotlin 实现上述行为的方法是什么?我试过 suspend
和 async
但他们似乎仍然会阻止我的请求线程,如果不等待昂贵的操作我就无法获得我的 API 响应
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): Response {
backgroundOpsScope.launch {
... expensive operation ...
}
}