使用ktor HTTP服务器,我想启动一个长时间运行的任务,并立即向调用客户端返回一条消息。该任务是自给自足的,它能够在数据库中更新其状态,并且单独的HTTP调用返回其状态(即进度条(。
我似乎不能做的只是在后台启动任务并做出回应。我所有的响应尝试都在等待长时间运行的任务完成。我已经尝试过runBlocking
和coroutineScope
的许多配置,但没有一个对我有效
// ktor route
get("/launchlongtask") {
val text: String = (myFunction(call.request.queryParameters["loops"]!!.toInt()))
println("myFunction returned")
call.respondText(text)
}
// in reality, this function is complex... the caller (route) is not able to
// determine the response string, it must be done here
suspend fun myFunction(loops : Int) : String {
runBlocking {
launch {
// long-running task, I want to launch it and move on
(1..loops).forEach {
println("this is loop $it")
delay(2000L)
// updates status in db here
}
}
println("returning")
// this string must be calculated in this function (or a sub-function)
return@runBlocking "we just launched $loops loops"
}
return "never get here" // actually we do get here in a coroutineScope
}
输出:
returning
this is loop 1
this is loop 2
this is loop 3
this is loop 4
myFunction returned
预期:
returning
myFunction returned
(response sent)
this is loop 1
this is loop 2
this is loop 3
this is loop 4
受到Lucas Milotich这个答案的启发,我使用了CoroutineScope(Job())
,它似乎起了作用:
suspend fun myFunction(loops : Int) : String {
CoroutineScope(Job()).launch {
// long-running task, I want to launch it and move on
(1..loops).forEach {
println("this is loop $it")
delay(2000L)
// updates status in db here
}
}
println("returning")
return "we just launched $loops loops"
}
不确定这是否是资源高效的,或者是首选的方法,但我没有看到很多关于这个主题的其他文档。
为了解释问题中的代码问题,问题是使用runBlocking
。这是协同程序和的同步世界和异步世界之间的桥梁
"runBlocking的名称意味着运行它的线程。。。在调用期间被阻止,直到runBlocking{…}内的所有协程都完成执行">
(来自Coroutine文档(。
因此,在您的第一个示例中,myFunction
直到包含循环的协程完成后才会完成。
正确的方法是在回答中使用CoroutineScope启动长期运行的任务。需要指出的一点是,您只是将Job()
作为CoroutineContext
参数传递给CoroutineScope
构造函数。CoroutineContext
包含多个内容;Job
、CoroutineDispatcher
、CoroutineExceptionHandler
。。。在这种情况下,因为您没有指定CoroutineDispatcher
,所以它将使用CoroutineDispatcher.Default
。这是为CPU密集型任务而设计的;CPU核的数量(最小为2个(";。这可能是你想要的,也可能不是你想要的。另一种选择是CoroutineDispatcher.IO,它默认有64个线程。