Kotlin 如何正确加入在协同作用域中启动的所有作业
我正在将当前在Kotlin 如何正确加入在协同作用域中启动的所有作业,kotlin,kotlin-coroutines,coroutinescope,Kotlin,Kotlin Coroutines,Coroutinescope,我正在将当前在GlobalScope上启动协同路由的一些Kotlin代码重构为基于结构化并发的方法。在JVM退出之前,我需要加入代码中启动的所有作业。我的类可以分解为以下接口: 接口异步任务器{ 有趣的工作(arg:Long) 暂停所有 } 用法: fun main(args:Array){ val asyncTasker=createAsyncTasker() asyncTasker.spawnJob(100) asyncTasker.spawnJob(200) asyncTasker.sp
GlobalScope
上启动协同路由的一些Kotlin代码重构为基于结构化并发的方法。在JVM退出之前,我需要加入代码中启动的所有作业。我的类可以分解为以下接口:
接口异步任务器{
有趣的工作(arg:Long)
暂停所有
}
用法:
fun main(args:Array){
val asyncTasker=createAsyncTasker()
asyncTasker.spawnJob(100)
asyncTasker.spawnJob(200)
asyncTasker.spawnJob(300)
asyncTasker.spawnJob(500)
//加入所有作业,因为它们将在JVM退出时被杀死
运行阻塞{
asyncTasker.joinAll()
}
}
基于我的GlobalScope
的实现如下所示:
class GlobalScopeAsyncTasker:AsyncTasker{
private val pendingJobs=mutableSetOf()
覆盖作业(参数:长){
变量作业:作业?=null
job=GlobalScope.launch(Dispatchers.IO){
someSuspendFun(arg)
挂起作业。删除(作业)
}
挂起作业。添加(作业)
}
覆盖挂起所有(){
//迭代集合的副本作为
//当我们加入作业时,作业将从集合中移除
pendingJobs.toSet().joinAll()
}
}
显然,这并不理想,因为跟踪每一个待处理的作业并不是很优雅,而且是旧的基于线程的编码范例的残余
作为一种更好的方法,我正在创建自己的CoroutineScope
,用于启动所有子项,提供一个SupervisorJob
类结构DConcurrencyAsyncTasker:AsyncTasker{
private val parentJob=SupervisorJob()
private val scope=CoroutineScope(Dispatchers.IO+parentJob)
覆盖作业(参数:长){
范围.发射{
someSuspendFun(arg)
}
}
覆盖挂起所有(){
parentJob.complete()//来自Job#join()
:
当作业因任何原因完成时,[…]将继续此调用
由于我从未将父作业标记为Completed
,join
永远不会返回,即使作业的所有子作业都已完成
考虑到作业无法将状态从已完成
切换回活动
,这是有意义的,因此,如果在所有子作业都已完成时,作业自动将状态切换到已完成
,则不可能在以后添加更多子作业
感谢您为我指明了正确的方向。我想知道这种行为将来是否会改变。目前,链接问题的答案仍然有效。目前,parentJob.join()
不加入其子项。对我来说,以下部分是深入挖掘的原因:
请注意,只有当作业的所有子项都完成时,作业才会完成
请注意,启动的协同程序作业可能处于“已完成”之外的另一种状态。您可能希望在执行parentJob.children.forEach{println(it)}
(或您希望检查或调试它的任何信息;-)之前,通过类似于parentJob.join()
-语句的方式验证这一点
有(至少?)两种方法可以确保所有已启动的子协同程序作业都已完成,这样它就不会挂起或过早完成:
parentJob.join()
或parentJob.complete()
,因此可能是首选的parentJob
将在其所有子项完成时完成
加入之前调用完成
,即:
parentJob.complete()
parentJob.join()
请注意,此处调用complete
只是将状态转换为completing,正如中所述。在completing状态下,它也将等待其子项完成。如果您只调用complete()
如果没有join
程序可能会退出,甚至在运行您启动的协同程序作业之前。如果您仅使用join()
它可能会无限期挂起,就像您已经体验到的那样
这是否回答了您的问题?如果您没有明确说明父作业或其子作业已完成,它将永远运行…即,如果您只是在父作业上调用
join
,它将等待所有子协同程序完成(文档中也有说明)。但是,启动的协同程序作业仍处于活动状态(或者至少没有完成)这就是为什么父作业挂在那里…但是我不知道为什么这样设计…@Roland来自作业文档:“协同程序作业是用启动协同程序生成器创建的。它运行指定的代码块,并在该块完成时完成。”由于当我加入时,someSuspendFun
已经返回,子作业已经完成。事实上,它们甚至不再在parentJob
的children
序列中。因此,如果我理解正确,父作业上的join
只是挂起,因为它等待父作业本身完成,而我必须手动启动?奇怪的设计,但我想这是有道理的。我明白了为什么这种行为是有道理的。请查看我自己对这个问题的回答。感谢您的输入,@Roland!想发表评论……但时间比预期的长,所以我添加了一个答案……您可能需要在调用parentJob.joi之前验证作业的实际状态n()
parentJob.complete()
parentJob.join()